diff --git a/BUILD.md b/BUILD.md
deleted file mode 100644
index 4d6d9bf..0000000
--- a/BUILD.md
+++ /dev/null
@@ -1,144 +0,0 @@
-# Windows 安装包构建指南
-
-## 环境要求
-
-- Ubuntu 24.04 (amd64)
-- Node.js v22+
-- Wine 9.0+(用于 electron-builder 交叉编译)
-- i386 架构已启用(`dpkg --add-architecture i386`)
-
-## 构建步骤
-
-### 1. 克隆项目
-
-```bash
-git clone https://gitee.com/thzxx/metona-ollama.git
-cd metona-ollama
-git checkout metona-ollama-desktop-v1.1
-```
-
-### 2. 安装依赖(使用国内镜像)
-
-```bash
-npm config set registry https://registry.npmmirror.com
-npm install
-```
-
-> ⚠️ **不要用默认 npm 源**,国内环境下大概率超时。npmmirror 速度快且稳定。
-
-### 3. 构建 Windows 安装包
-
-```bash
-ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
-```
-
-> ⚠️ **必须设置 `ELECTRON_MIRROR`**,否则 electron-builder 会从 GitHub 下载 Electron 二进制(115MB),国内基本连不上。
-
-## 遇到的问题与解决方案
-
-### 问题 1:apt 源不可用(阿里云镜像超时)
-
-**现象**:`apt-get update` 报错 `mirrors.cloud.aliyuncs.com` 连接超时。
-
-**解决**:替换为 Ubuntu 官方源:
-
-```bash
-sed -i 's|http://mirrors.cloud.aliyuncs.com/ubuntu|http://archive.ubuntu.com/ubuntu|g' /etc/apt/sources.list
-```
-
-### 问题 2:npm install 超时
-
-**现象**:使用默认 npm registry 下载依赖超时。
-
-**解决**:切换到 npmmirror 国内镜像:
-
-```bash
-npm config set registry https://registry.npmmirror.com
-```
-
-### 问题 3:Electron 下载超时(GitHub 连不上)
-
-**现象**:electron-builder 打包时卡在 `downloading electron-v33.4.11-win32-x64.zip`,115MB 从 GitHub 下载,国内超时。
-
-**解决**:设置环境变量使用 npmmirror 的 Electron 镜像:
-
-```bash
-ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm run dist
-```
-
-设置后 4.22 秒下载完成,vs 之前直接超时。
-
-### 问题 4:winCodeSign 下载慢
-
-**现象**:electron-builder 需要从 GitHub 下载 `winCodeSign-2.6.0`(5.6MB)。
-
-**说明**:这个文件较小,即使从 GitHub 下载通常也能完成(可能需要几分钟)。如果超时,可以手动下载放到缓存目录:
-
-```bash
-# electron-builder 缓存目录
-~/.cache/electron-builder/winCodeSign/winCodeSign-2.6.0/
-```
-
-## 常用命令
-
-```bash
-# 开发运行
-npm start
-
-# 仅构建目录(不打包,用于测试)
-npm run pack
-
-# 构建 NSIS 安装包
-npm run dist:nsis
-
-# 构建绿色便携版
-npm run dist:portable
-
-# 构建全部(NSIS + Portable)
-npm run dist
-```
-
-## 构建产物
-
-| 文件 | 说明 |
-|------|------|
-| `release/Metona Ollama Setup X.X.X.exe` | NSIS 安装包(可选目录、创建快捷方式) |
-| `release/MetonaOllama-Portable-X.X.X.exe` | 绿色便携版(免安装,双击即用) |
-
-## 发布到 Gitee
-
-### 上传附件
-
-```bash
-TOKEN="your_access_token"
-RELEASE_ID="600168" # v1.1.0 release ID
-
-curl -X POST \
- "https://gitee.com/api/v5/repos/thzxx/metona-ollama/releases/$RELEASE_ID/attach_files?access_token=$TOKEN" \
- -H "Content-Type: multipart/form-data" \
- -F "file=@release/Metona Ollama Setup 1.1.0.exe"
-
-curl -X POST \
- "https://gitee.com/api/v5/repos/thzxx/metona-ollama/releases/$RELEASE_ID/attach_files?access_token=$TOKEN" \
- -H "Content-Type: multipart/form-data" \
- -F "file=@release/MetonaOllama-Portable-1.1.0.exe"
-```
-
-### 清理重复附件
-
-```bash
-# 查看附件列表
-curl -s "https://gitee.com/api/v5/repos/thzxx/metona-ollama/releases/$RELEASE_ID/attach_files?access_token=$TOKEN"
-
-# 删除指定附件
-curl -X DELETE "https://gitee.com/api/v5/repos/thzxx/metona-ollama/releases/$RELEASE_ID/attach_files/$ASSET_ID?access_token=$TOKEN"
-```
-
-## 提速清单
-
-| 环节 | 默认方案 | 加速方案 | 效果 |
-|------|----------|----------|------|
-| apt 源 | archive.ubuntu.com | 保持官方源即可 | 稳定可用 |
-| npm 依赖 | registry.npmjs.org | registry.npmmirror.com | 59s 完成 |
-| Electron 下载 | github.com | npmmirror.com/mirrors/electron | 4s 完成(vs 超时) |
-| winCodeSign | github.com | 小文件可等待 / 手动缓存 | 通常可完成 |
diff --git a/DESKTOP_README.md b/DESKTOP_README.md
deleted file mode 100644
index 57fccb9..0000000
--- a/DESKTOP_README.md
+++ /dev/null
@@ -1,119 +0,0 @@
-# Metona Ollama Desktop v1.0
-
-基于 [Metona Ollama](https://gitee.com/thzxx/metona-ollama) Web 版封装的 **Electron Windows 桌面客户端**。
-
-## ✨ 桌面版特性
-
-| 特性 | 说明 |
-|------|------|
-| 原生安装包 | NSIS 安装程序 + 绿色便携版 |
-| 系统托盘 | 最小化到托盘,双击恢复,托盘右键菜单 |
-| 原生菜单 | 完整的文件/编辑/视图/窗口/帮助菜单 |
-| 原生文件对话框 | Windows 文件选择器替代浏览器 input |
-| 系统通知 | 通过 Windows 原生通知中心推送 |
-| 窗口管理 | 记忆窗口大小和位置,支持置顶 |
-| 单实例锁 | 防止重复启动 |
-| 无 CORS 限制 | 关闭 webSecurity,直接连接本地 Ollama |
-
-## 🚀 开发
-
-### 前置条件
-
-- Node.js 18+
-- npm
-
-### 启动开发环境
-
-```bash
-# 安装依赖
-npm install
-
-# 启动开发模式
-npm start
-```
-
-### 构建
-
-```bash
-# 构建目录(不打包,用于测试)
-npm run pack
-
-# 构建 Windows 安装包 (NSIS + Portable)
-npm run dist
-
-# 仅构建 NSIS 安装包
-npm run dist:nsis
-
-# 仅构建便携版
-npm run dist:portable
-```
-
-构建产物在 `release/` 目录。
-
-## 📁 Desktop 项目结构
-
-```
-metona-ollama/
-├── electron/
-│ ├── main.js # 主进程:窗口、菜单、托盘、IPC
-│ ├── preload.js # 预加载:contextBridge 安全 API 暴露
-│ ├── desktop-bridge.js # 渲染进程桥接:统一 API + 降级处理
-│ └── desktop-integration.js # 桌面集成:菜单响应、原生文件选择、快捷键
-├── index.html # 入口(已集成桌面脚本)
-├── js/ # 原有 Web 应用代码(未修改)
-├── css/ # 原有样式(未修改)
-├── assets/ # 图标资源
-├── package.json # Electron + electron-builder 配置
-└── ...
-```
-
-## 🔌 架构设计
-
-```
-┌──────────────────────────────────────────────┐
-│ Main Process │
-│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
-│ │ Browser │ │ Tray │ │ Menu │ │
-│ │ Window │ │ │ │ │ │
-│ └────┬─────┘ └────┬─────┘ └──────┬───────┘ │
-│ │ │ │ │
-│ ┌────┴─────────────┴──────────────┴───────┐ │
-│ │ IPC Main │ │
-│ └────────────────┬────────────────────────┘ │
-└───────────────────┼──────────────────────────┘
- │ contextBridge
-┌───────────────────┼──────────────────────────┐
-│ Renderer │ │
-│ ┌────────────────┴───────────────────────┐ │
-│ │ window.metonaDesktop (preload.js) │ │
-│ └────────────────┬───────────────────────┘ │
-│ ┌────────────────┴───────────────────────┐ │
-│ │ window.__metonaBridge (bridge.js) │ │
-│ │ 统一接口 + 降级 (Web环境可用) │ │
-│ └────────────────┬───────────────────────┘ │
-│ ┌────────────────┴───────────────────────┐ │
-│ │ desktop-integration.js │ │
-│ │ 菜单响应 · 原生文件 · 快捷键 │ │
-│ └────────────────┬───────────────────────┘ │
-│ ┌────────────────┴───────────────────────┐ │
-│ │ 原有 Web 应用 (app.js + components) │ │
-│ │ 零修改,完全复用 │ │
-│ └────────────────────────────────────────┘ │
-└──────────────────────────────────────────────┘
-```
-
-## 🔒 安全策略
-
-- `contextIsolation: true` — 渲染进程无 Node.js 访问
-- `nodeIntegration: false` — 通过 preload 安全暴露 API
-- `sandbox: false` — 允许文件系统访问(受 IPC 白名单控制)
-- `webSecurity: false` — 仅用于连接本地 Ollama API
-
-## 📝 与 Web 版的兼容性
-
-桌面版完全兼容 Web 版代码,通过降级处理实现:
-
-- `window.__metonaBridge.isDesktop` 判断当前环境
-- Web 环境下所有 desktop API 自动降级为浏览器原生行为
-- `index.html` 无条件加载桌面脚本,非 Electron 环境静默跳过
-- 原有 JS/HTML/CSS 代码**零修改**
diff --git a/css/style.css b/css/style.css
deleted file mode 100644
index 9198df5..0000000
--- a/css/style.css
+++ /dev/null
@@ -1,2183 +0,0 @@
-/* ═══════════════════════════════════════════════════════════════
- Metona Ollama — Windows 11 Fluent Design 暗色主题
- Mica 材质 · Segoe UI Variable · WinUI 3 控件风格
- ═══════════════════════════════════════════════════════════════ */
-
-/* ── CSS 变量 ── */
-:root {
- /* Windows 11 暗色调色板 */
- --bg-solid: #202020;
- --bg-card: #2d2d2d;
- --bg-card-hover: #323232;
- --bg-layer: #383838;
- --bg-layer-alt: #2a2a2a;
- --bg-smoke: rgba(0, 0, 0, 0.3);
- --bg-mica: rgba(32, 32, 32, 0.72);
- --bg-acrylic: rgba(32, 32, 32, 0.8);
-
- --border-subtle: rgba(255, 255, 255, 0.06);
- --border-default: rgba(255, 255, 255, 0.08);
- --border-strong: rgba(255, 255, 255, 0.12);
- --border-focus: #60CDFF;
-
- --text-primary: #FFFFFF;
- --text-secondary: #999999;
- --text-tertiary: #666666;
- --text-disabled: #505050;
-
- --accent: #60CDFF;
- --accent-hover: #7AD8FF;
- --accent-subtle: rgba(96, 205, 255, 0.06);
- --accent-subtle-hover: rgba(96, 205, 255, 0.1);
-
- --critical: #F1707B;
- --critical-bg: rgba(241, 112, 123, 0.06);
- --success: #6CCB5F;
- --success-bg: rgba(108, 203, 95, 0.06);
- --caution: #FCE100;
- --caution-bg: rgba(252, 225, 0, 0.06);
-
- /* 圆角 — Windows 11 标准 */
- --radius-control: 4px;
- --radius-sm: 8px;
- --radius-md: 8px;
- --radius-lg: 12px;
- --radius-xl: 16px;
-
- /* 阴影 — Fluent Design 深度 */
- --shadow-flyout: 0 8px 16px rgba(0, 0, 0, 0.14);
- --shadow-dialog: 0 24px 48px rgba(0, 0, 0, 0.18);
- --shadow-tooltip: 0 4px 8px rgba(0, 0, 0, 0.14);
-
- /* 字体 */
- --font: 'Segoe UI Variable', 'Segoe UI', system-ui, -apple-system, sans-serif;
- --font-mono: 'Cascadia Mono', 'Cascadia Code', 'Consolas', monospace;
-
- --transition: 167ms cubic-bezier(0, 0, 0, 1);
- --transition-fast: 83ms cubic-bezier(0, 0, 0, 1);
-}
-
-/* ── 全局重置 ── */
-*, *::before, *::after {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
-}
-
-html, body {
- height: 100%;
- overflow: hidden;
- background: var(--bg-solid);
- color: var(--text-primary);
- font-family: var(--font);
- font-size: 14px;
- line-height: 1.5;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-#app {
- display: flex;
- flex-direction: column;
- height: 100vh;
-}
-
-/* ═══ 顶部导航 ═══ */
-.app-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 8px 16px;
- background: var(--bg-mica);
- backdrop-filter: saturate(180%) blur(20px);
- -webkit-backdrop-filter: saturate(180%) blur(20px);
- border-bottom: 1px solid var(--border-subtle);
- flex-shrink: 0;
- z-index: 10;
-}
-
-.header-left {
- display: flex;
- align-items: center;
- gap: 10px;
- min-width: 0;
- flex: 1;
-}
-
-.logo {
- font-size: 20px;
- flex-shrink: 0;
-}
-
-.app-title {
- font-size: 14px;
- font-weight: 600;
- color: var(--text-primary);
- white-space: nowrap;
-}
-
-.app-version {
- font-size: 10px;
- font-weight: 600;
- color: var(--text-secondary);
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-radius: 12px;
- padding: 1px 8px;
- white-space: nowrap;
- user-select: none;
-}
-
-.header-right {
- display: flex;
- align-items: center;
- gap: 4px;
-}
-
-/* ── 连接状态 ── */
-.conn-status {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 10px;
- border-radius: var(--radius-control);
- font-size: 12px;
- color: var(--text-secondary);
-}
-
-.status-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- transition: var(--transition);
-}
-
-.status-label {
- white-space: nowrap;
-}
-
-.conn-status.connected .status-dot {
- background: var(--success);
- box-shadow: 0 0 4px var(--success);
-}
-
-.conn-status.disconnected .status-dot {
- background: var(--critical);
-}
-
-.conn-status.pending .status-dot {
- background: var(--caution);
- animation: blink 1s ease-in-out infinite;
-}
-
-@keyframes blink {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.4; }
-}
-
-/* ── 图标按钮 (WinUI 3 风格) ── */
-.icon-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 36px;
- height: 36px;
- border: none;
- background: transparent;
- color: var(--text-secondary);
- border-radius: var(--radius-control);
- cursor: pointer;
- transition: var(--transition);
- position: relative;
-}
-
-.icon-btn::before {
- content: '';
- position: absolute;
- inset: 0;
- border-radius: var(--radius-control);
- background: currentColor;
- opacity: 0;
- transition: var(--transition);
-}
-
-.icon-btn:hover::before {
- opacity: 0.06;
-}
-
-.icon-btn:active::before {
- opacity: 0.04;
-}
-
-.icon-btn:hover {
- color: var(--text-primary);
-}
-
-.icon-btn:active {
- transform: scale(0.97);
-}
-
-.icon-btn svg {
- width: 18px;
- height: 18px;
- position: relative;
- z-index: 1;
-}
-
-.icon-btn.disabled,
-.icon-btn:disabled {
- opacity: 0.36;
- cursor: not-allowed;
-}
-
-/* 图片按钮禁用时保留 pointer-events 以显示 tooltip 和 cursor */
-#btnAttachImg.disabled {
- pointer-events: auto;
-}
-
-#btnAttachImg.disabled:hover {
- opacity: 0.3;
- background: rgba(241, 112, 123, 0.08);
-}
-
-.icon-btn.sm {
- width: 28px;
- height: 28px;
-}
-
-.icon-btn.sm svg {
- width: 14px;
- height: 14px;
-}
-
-.icon-btn.danger:hover {
- color: var(--critical);
-}
-
-.help-btn {
- margin-left: 2px;
- opacity: 0.5;
-}
-
-.help-btn:hover {
- opacity: 1;
-}
-
-/* ═══ 模型选择栏 ═══ */
-.model-bar {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 8px 16px;
- background: var(--bg-layer-alt);
- border-bottom: 1px solid var(--border-subtle);
- flex-shrink: 0;
-}
-
-.model-bar-icon {
- width: 28px;
- height: 28px;
- border-radius: var(--radius-control);
- background: var(--accent-subtle);
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.model-bar-icon svg {
- width: 14px;
- height: 14px;
- color: var(--accent);
-}
-
-.model-select-wrap {
- position: relative;
- flex: 1;
- min-width: 0;
- display: flex;
- align-items: center;
-}
-
-.model-select {
- width: 100%;
- padding: 6px 32px 6px 12px;
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-control);
- color: var(--text-primary);
- font-size: 13px;
- font-family: var(--font);
- outline: none;
- cursor: pointer;
- transition: var(--transition);
- appearance: none;
- -webkit-appearance: none;
-}
-
-.model-select:focus {
- border-color: var(--border-focus);
- box-shadow: 0 0 0 1px var(--border-focus);
-}
-
-.model-select:hover {
- border-color: var(--border-strong);
-}
-
-.model-select option {
- background: var(--bg-card);
- color: var(--text-primary);
-}
-
-.model-select-arrow {
- position: absolute;
- right: 8px;
- top: 50%;
- transform: translateY(-50%);
- pointer-events: none;
- color: var(--text-tertiary);
-}
-
-.model-select-arrow svg {
- width: 14px;
- height: 14px;
-}
-
-.model-badges {
- display: flex;
- gap: 6px;
- flex-shrink: 0;
-}
-
-.model-badge {
- font-size: 11px;
- font-weight: 500;
- white-space: nowrap;
- padding: 3px 8px;
- border-radius: var(--radius-control);
- border: 1px solid;
-}
-
-.think-badge {
- color: var(--accent);
- background: var(--accent-subtle);
- border-color: rgba(96, 205, 255, 0.15);
-}
-
-.vision-badge {
- color: var(--success);
- background: var(--success-bg);
- border-color: rgba(108, 203, 95, 0.15);
-}
-
-.rag-badge {
- color: #B388FF;
- background: rgba(179, 136, 255, 0.06);
- border-color: rgba(179, 136, 255, 0.15);
-}
-
-/* ── 预设栏 ── */
-.preset-bar {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 6px 16px;
- background: var(--bg-layer-alt);
- border-bottom: 1px solid var(--border-subtle);
- flex-shrink: 0;
-}
-
-.preset-list {
- display: flex;
- gap: 6px;
- flex: 1;
- overflow-x: auto;
- overflow-y: hidden;
- scrollbar-width: none;
- -ms-overflow-style: none;
- padding: 2px 0;
-}
-
-.preset-list::-webkit-scrollbar { display: none; }
-
-.preset-chip {
- display: flex;
- align-items: center;
- gap: 5px;
- padding: 4px 12px;
- border-radius: var(--radius-control);
- border: 1px solid var(--border-default);
- background: var(--bg-layer);
- color: var(--text-secondary);
- font-size: 12px;
- font-family: var(--font);
- white-space: nowrap;
- cursor: pointer;
- transition: var(--transition);
- flex-shrink: 0;
- user-select: none;
-}
-
-.preset-chip:hover {
- background: var(--bg-card-hover);
- border-color: var(--border-strong);
- color: var(--text-primary);
-}
-
-.preset-chip.active {
- background: var(--accent-subtle);
- border-color: var(--accent);
- color: var(--accent);
- font-weight: 600;
-}
-
-.preset-chip.active:hover {
- background: var(--accent-subtle-hover);
-}
-
-.preset-chip-icon {
- font-size: 13px;
- line-height: 1;
-}
-
-.preset-chip-name {
- line-height: 1;
-}
-
-.preset-add-btn {
- width: 28px;
- height: 28px;
- border-radius: var(--radius-control);
- border: 1px dashed var(--border-default);
- background: transparent;
- color: var(--text-tertiary);
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- transition: var(--transition);
- flex-shrink: 0;
-}
-
-.preset-add-btn:hover {
- border-color: var(--accent);
- color: var(--accent);
- background: var(--accent-subtle);
-}
-
-.preset-add-btn svg {
- width: 14px;
- height: 14px;
-}
-
-/* ── 预设模态框 ── */
-.preset-name-row {
- display: flex;
- gap: 8px;
- align-items: center;
-}
-
-.preset-icon-input {
- width: 52px !important;
- text-align: center;
- font-size: 16px;
- flex-shrink: 0;
- padding: 6px 4px !important;
-}
-
-.preset-temp-slider {
- width: 100%;
- -webkit-appearance: none;
- appearance: none;
- height: 4px;
- border-radius: 2px;
- background: var(--bg-layer);
- outline: none;
- margin: 8px 0;
-}
-
-.preset-temp-slider::-webkit-slider-thumb {
- -webkit-appearance: none;
- width: 16px;
- height: 16px;
- border-radius: 50%;
- background: var(--accent);
- cursor: pointer;
- box-shadow: 0 0 0 4px transparent;
- transition: box-shadow var(--transition);
-}
-
-.preset-temp-slider::-webkit-slider-thumb:hover {
- box-shadow: 0 0 0 3px var(--accent-subtle);
-}
-
-.preset-temp-slider::-moz-range-thumb {
- width: 16px;
- height: 16px;
- border-radius: 50%;
- background: var(--accent);
- cursor: pointer;
- border: none;
-}
-
-.preset-modal-actions {
- display: flex;
- gap: 8px;
- align-items: center;
- margin-top: 16px;
- padding-top: 12px;
- border-top: 1px solid var(--border-subtle);
-}
-
-/* ── Think 按钮 ── */
-.think-toggle {
- display: flex;
- align-items: center;
- cursor: pointer;
- flex-shrink: 0;
- user-select: none;
-}
-
-.think-toggle input { display: none; }
-
-.think-btn {
- width: 36px;
- height: 36px;
- border-radius: var(--radius-control);
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- display: flex;
- align-items: center;
- justify-content: center;
- transition: var(--transition);
- color: var(--text-tertiary);
-}
-
-.think-btn svg {
- width: 18px;
- height: 18px;
-}
-
-.think-toggle:not(.unavailable):hover .think-btn {
- border-color: var(--border-strong);
- color: var(--text-secondary);
-}
-
-.think-toggle input:checked + .think-btn {
- background: var(--accent-subtle);
- border-color: var(--accent);
- color: var(--accent);
- box-shadow: 0 0 12px rgba(96, 205, 255, 0.4), 0 0 4px rgba(96, 205, 255, 0.2);
-}
-
-.think-toggle input:checked + .think-btn:hover {
- background: var(--accent-subtle-hover);
- box-shadow: 0 0 18px rgba(96, 205, 255, 0.55), 0 0 6px rgba(96, 205, 255, 0.3);
-}
-
-.think-toggle.unavailable .think-btn {
- opacity: 0.36;
- cursor: not-allowed;
-}
-
-.think-toggle.unavailable:hover .think-btn {
- opacity: 0.3;
- background: rgba(241, 112, 123, 0.08);
-}
-
-/* ── 开关控件 (WinUI ToggleSwitch) ── */
-.toggle-label {
- display: flex;
- align-items: center;
- gap: 8px;
- font-size: 12px;
- color: var(--text-secondary);
- cursor: pointer;
- user-select: none;
- white-space: nowrap;
-}
-
-.toggle-label input { display: none; }
-
-.toggle-slider {
- position: relative;
- width: 40px;
- height: 20px;
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-radius: 10px;
- transition: var(--transition);
-}
-
-.toggle-slider::after {
- content: '';
- position: absolute;
- top: 2px;
- left: 2px;
- width: 14px;
- height: 14px;
- background: var(--text-secondary);
- border-radius: 50%;
- transition: var(--transition);
-}
-
-.toggle-label input:checked + .toggle-slider {
- background: var(--accent);
- border-color: var(--accent);
-}
-
-.toggle-label input:checked + .toggle-slider::after {
- transform: translateX(20px);
- background: var(--bg-solid);
-}
-
-/* ═══ 聊天区域 ═══ */
-.chat-area {
- flex: 1;
- overflow-y: auto;
- overflow-x: hidden;
- padding: 16px;
- scroll-behavior: smooth;
-}
-
-.chat-area::-webkit-scrollbar {
- width: 8px;
-}
-
-.chat-area::-webkit-scrollbar-track {
- background: transparent;
-}
-
-.chat-area::-webkit-scrollbar-thumb {
- background: var(--border-default);
- border-radius: 4px;
-}
-
-.chat-area::-webkit-scrollbar-thumb:hover {
- background: var(--border-strong);
-}
-
-/* ── 回到底部按钮 ── */
-.scroll-to-bottom {
- position: sticky;
- bottom: 16px;
- float: right;
- margin-right: 4px;
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 6px 14px;
- border: 1px solid var(--border-default);
- background: var(--bg-card);
- backdrop-filter: blur(20px);
- border-radius: var(--radius-control);
- color: var(--text-secondary);
- font-size: 12px;
- font-family: var(--font);
- cursor: pointer;
- z-index: 5;
- box-shadow: var(--shadow-flyout);
- transition: var(--transition);
- animation: fadeSlideUp 167ms ease;
-}
-
-.scroll-to-bottom:hover {
- background: var(--bg-card-hover);
- border-color: var(--border-strong);
- color: var(--text-primary);
-}
-
-.scroll-to-bottom:active {
- transform: scale(0.98);
-}
-
-.scroll-to-bottom svg {
- width: 14px;
- height: 14px;
-}
-
-@keyframes fadeSlideUp {
- from { opacity: 0; transform: translateY(8px); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-/* ── 空状态 ── */
-.empty-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 100%;
- gap: 12px;
- color: var(--text-tertiary);
-}
-
-.empty-icon svg {
- width: 80px;
- height: 80px;
- opacity: 0.3;
- color: var(--text-tertiary);
-}
-
-.empty-state h2 {
- font-size: 20px;
- font-weight: 600;
- color: var(--text-primary);
-}
-
-.empty-state p {
- font-size: 14px;
- color: var(--text-tertiary);
-}
-
-/* ── 消息气泡 ── */
-.messages {
- display: flex;
- flex-direction: column;
- gap: 12px;
- max-width: 780px;
- margin: 0 auto;
-}
-
-.message {
- display: flex;
- gap: 12px;
- animation: fadeSlideUp 167ms ease;
-}
-
-.msg-avatar {
- flex-shrink: 0;
- width: 32px;
- height: 32px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- border-radius: var(--radius-control);
- background: var(--bg-layer);
- border: 1px solid var(--border-subtle);
-}
-
-.msg-body {
- flex: 1;
- min-width: 0;
-}
-
-.msg-role {
- font-size: 12px;
- font-weight: 600;
- color: var(--text-tertiary);
- margin-bottom: 4px;
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.model-tag {
- display: inline-block;
- font-size: 11px;
- font-weight: 600;
- color: var(--accent);
- background: var(--accent-subtle);
- border: 1px solid rgba(96, 205, 255, 0.15);
- border-radius: var(--radius-control);
- padding: 1px 8px;
- line-height: 1.5;
- white-space: nowrap;
-}
-
-.message.user .msg-body {
- background: var(--bg-layer);
- border: 1px solid var(--border-subtle);
- border-radius: var(--radius-md);
- padding: 10px 14px;
-}
-
-.message.assistant .msg-body {
- background: var(--bg-card);
- border: 1px solid var(--border-subtle);
- border-radius: var(--radius-md);
- padding: 10px 14px;
-}
-
-.msg-content {
- font-size: 14px;
- line-height: 1.65;
- word-break: break-word;
-}
-
-.msg-content pre {
- background: var(--bg-solid);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-control);
- padding: 12px 14px;
- overflow-x: auto;
- font-family: var(--font-mono);
- font-size: 13px;
- margin: 8px 0;
-}
-
-.msg-content code {
- background: rgba(255, 255, 255, 0.06);
- padding: 1px 5px;
- border-radius: 3px;
- font-family: var(--font-mono);
- font-size: 0.9em;
-}
-
-.msg-content pre code {
- background: none;
- padding: 0;
-}
-
-.msg-content p { margin-bottom: 8px; }
-.msg-content p:last-child { margin-bottom: 0; }
-
-/* 思考块 */
-.think-block {
- margin-bottom: 8px;
- border: 1px solid rgba(252, 225, 0, 0.12);
- border-radius: var(--radius-control);
- overflow: hidden;
-}
-
-.think-header {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 6px 10px;
- background: var(--caution-bg);
- color: var(--caution);
- font-size: 12px;
- cursor: pointer;
- user-select: none;
-}
-
-.think-header .chevron {
- margin-left: auto;
- transition: transform var(--transition);
-}
-
-.think-header .chevron.expanded {
- transform: rotate(180deg);
-}
-
-.think-content {
- padding: 8px 12px;
- background: rgba(252, 225, 0, 0.02);
-}
-
-.think-content pre {
- font-size: 12px;
- color: var(--text-secondary);
- white-space: pre-wrap;
- word-break: break-word;
- margin: 0;
- background: none;
- border: none;
- padding: 0;
-}
-
-/* 打字光标 */
-.typing-cursor {
- display: inline-block;
- width: 2px;
- height: 1em;
- background: var(--accent);
- animation: caretBlink 1s step-end infinite;
- vertical-align: text-bottom;
- margin-left: 1px;
-}
-
-@keyframes caretBlink {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0; }
-}
-
-/* ═══ Loading 动画 ═══ */
-.loading-dots {
- display: inline-flex;
- align-items: center;
- gap: 5px;
- padding: 8px 0;
-}
-
-.loading-dots span {
- width: 6px;
- height: 6px;
- border-radius: 50%;
- background: var(--text-tertiary);
- animation: dotPulse 1.4s ease-in-out infinite;
-}
-
-.loading-dots span:nth-child(1) { animation-delay: 0s; }
-.loading-dots span:nth-child(2) { animation-delay: 0.2s; }
-.loading-dots span:nth-child(3) { animation-delay: 0.4s; }
-
-@keyframes dotPulse {
- 0%, 80%, 100% { transform: scale(0.6); opacity: 0.3; }
- 40% { transform: scale(1); opacity: 1; }
-}
-
-.loading-text {
- font-size: 12px;
- color: var(--text-tertiary);
- animation: loadingFade 1.5s ease-in-out infinite;
-}
-
-@keyframes loadingFade {
- 0%, 100% { opacity: 0.4; }
- 50% { opacity: 0.8; }
-}
-
-/* 消息图片 */
-.msg-images {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 8px;
-}
-
-.msg-img {
- max-width: 200px;
- max-height: 200px;
- border-radius: var(--radius-control);
- border: 1px solid var(--border-subtle);
- object-fit: cover;
-}
-
-.msg-img[data-lightbox] {
- cursor: zoom-in;
- transition: var(--transition);
-}
-
-.msg-img[data-lightbox]:hover {
- opacity: 0.85;
-}
-
-/* 文件 chip */
-.msg-files {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
- margin-top: 8px;
-}
-
-.msg-files .file-chip {
- background: var(--accent-subtle);
- border-color: rgba(96, 205, 255, 0.12);
-}
-
-/* 统计信息 */
-.msg-stats {
- display: flex;
- gap: 12px;
- margin-top: 6px;
- font-size: 11px;
- color: var(--text-tertiary);
-}
-
-/* RAG 知识库来源 */
-.rag-sources {
- margin-top: 8px;
- padding: 8px 12px;
- background: var(--accent-subtle);
- border: 1px solid rgba(96, 205, 255, 0.1);
- border-radius: var(--radius-control);
- font-size: 12px;
-}
-
-.rag-sources-header {
- color: var(--accent);
- font-weight: 600;
- margin-bottom: 4px;
- font-size: 11px;
-}
-
-.rag-source-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 3px 0;
- color: var(--text-tertiary);
-}
-
-.rag-source-item + .rag-source-item {
- border-top: 1px solid var(--border-subtle);
-}
-
-.rag-source-name {
- color: var(--text-secondary);
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.rag-source-score {
- color: var(--success);
- font-weight: 500;
- margin-left: 8px;
- flex-shrink: 0;
-}
-
-/* ═══ 输入区域 ═══ */
-.input-area {
- flex-shrink: 0;
- padding: 10px 16px 14px;
- background: var(--bg-mica);
- backdrop-filter: saturate(180%) blur(20px);
- -webkit-backdrop-filter: saturate(180%) blur(20px);
- border-top: 1px solid var(--border-subtle);
-}
-
-.image-preview {
- display: flex;
- gap: 8px;
- padding: 8px 0;
- flex-wrap: wrap;
-}
-
-.preview-thumb {
- position: relative;
- width: 56px;
- height: 56px;
- border-radius: var(--radius-control);
- overflow: hidden;
- border: 1px solid var(--border-default);
-}
-
-.preview-thumb img {
- width: 100%;
- height: 100%;
- object-fit: cover;
-}
-
-.remove-img {
- position: absolute;
- top: 2px;
- right: 2px;
- width: 18px;
- height: 18px;
- border: none;
- background: var(--critical);
- color: white;
- border-radius: 50%;
- font-size: 12px;
- line-height: 1;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-/* 文件预览 */
-.file-preview {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
- padding: 8px 0;
-}
-
-.file-chip {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 10px;
- border-radius: var(--radius-control);
- background: var(--accent-subtle);
- border: 1px solid rgba(96, 205, 255, 0.12);
- font-size: 12px;
- line-height: 1;
- transition: var(--transition);
- animation: fadeSlideUp 167ms ease;
-}
-
-.file-chip:hover {
- background: var(--accent-subtle-hover);
- border-color: rgba(96, 205, 255, 0.2);
-}
-
-.file-icon {
- font-size: 13px;
- flex-shrink: 0;
-}
-
-.file-name {
- font-weight: 600;
- color: var(--text-primary);
- max-width: 160px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.file-size {
- color: var(--text-tertiary);
- flex-shrink: 0;
-}
-
-.remove-file {
- background: none;
- border: none;
- color: var(--text-tertiary);
- cursor: pointer;
- font-size: 14px;
- line-height: 1;
- padding: 0 2px;
- border-radius: 3px;
- transition: var(--transition);
-}
-
-.remove-file:hover {
- color: var(--critical);
- background: var(--critical-bg);
-}
-
-.input-row {
- display: flex;
- align-items: flex-end;
- gap: 8px;
-}
-
-.attach-btn {
- flex-shrink: 0;
-}
-
-.chat-input {
- flex: 1;
- padding: 8px 12px;
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-control);
- color: var(--text-primary);
- font-family: var(--font);
- font-size: 14px;
- resize: none;
- outline: none;
- min-height: 36px;
- max-height: 120px;
- line-height: 1.5;
- transition: var(--transition);
-}
-
-.chat-input:focus {
- border-color: var(--border-focus);
- box-shadow: 0 0 0 1px var(--border-focus);
-}
-
-.chat-input::placeholder {
- color: var(--text-disabled);
-}
-
-.send-btn {
- flex-shrink: 0;
- width: 36px;
- height: 36px;
- border: none;
- border-radius: var(--radius-control);
- background: var(--accent);
- color: #1a1a1a;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: var(--transition);
-}
-
-.send-btn:hover {
- background: var(--accent-hover);
-}
-
-.send-btn:active {
- transform: scale(0.96);
-}
-
-.send-btn.disabled {
- opacity: 0.36;
- cursor: not-allowed;
- pointer-events: none;
-}
-
-.send-btn svg {
- width: 16px;
- height: 16px;
-}
-
-.send-btn.stop-btn {
- background: var(--critical);
- color: white;
-}
-
-.send-btn.stop-btn:hover {
- opacity: 0.9;
-}
-
-.spinner {
- width: 16px;
- height: 16px;
- border: 2px solid rgba(255, 255, 255, 0.3);
- border-top-color: white;
- border-radius: 50%;
- animation: spin 0.8s linear infinite;
-}
-
-@keyframes spin {
- to { transform: rotate(360deg); }
-}
-
-/* ═══ 历史只读提示栏 ═══ */
-.history-view-bar {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 6px 16px;
- background: rgba(96, 205, 255, 0.04);
- border-top: 1px solid rgba(96, 205, 255, 0.1);
- font-size: 13px;
- color: var(--accent);
- flex-shrink: 0;
-}
-
-/* ═══ 模态框 (WinUI ContentDialog 风格) ═══ */
-.modal-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.5);
- backdrop-filter: blur(16px);
- -webkit-backdrop-filter: blur(16px);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 100;
- animation: overlayIn 167ms ease;
- padding: 20px;
-}
-
-@keyframes overlayIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-.modal {
- background: var(--bg-card);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-lg);
- box-shadow: var(--shadow-dialog);
- width: 100%;
- max-width: 480px;
- max-height: 80vh;
- display: flex;
- flex-direction: column;
- animation: dialogIn 200ms cubic-bezier(0, 0, 0, 1);
- overflow: hidden;
-}
-
-@keyframes dialogIn {
- from { opacity: 0; transform: scale(0.96) translateY(8px); }
- to { opacity: 1; transform: scale(1) translateY(0); }
-}
-
-.modal-lg {
- max-width: 860px;
- width: 96vw;
- max-height: 85vh;
-}
-
-.modal-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16px 20px;
- border-bottom: 1px solid var(--border-subtle);
- flex-shrink: 0;
-}
-
-.modal-header h3 {
- font-size: 16px;
- font-weight: 600;
- color: var(--text-primary);
-}
-
-.modal-body {
- padding: 16px 20px;
- overflow-y: auto;
- flex: 1;
-}
-
-/* ── 设置项 ── */
-.setting-group {
- margin-bottom: 16px;
- padding-bottom: 16px;
- border-bottom: 1px solid var(--border-subtle);
-}
-
-.setting-group:last-child {
- border-bottom: none;
- margin-bottom: 0;
-}
-
-.setting-label {
- display: flex;
- align-items: center;
- font-size: 13px;
- font-weight: 600;
- color: var(--text-secondary);
- margin-bottom: 6px;
-}
-
-.setting-input {
- width: 100%;
- padding: 8px 10px;
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-control);
- color: var(--text-primary);
- font-size: 13px;
- font-family: var(--font);
- outline: none;
- margin-bottom: 6px;
- transition: var(--transition);
-}
-
-.setting-input:focus {
- border-color: var(--border-focus);
- box-shadow: 0 0 0 1px var(--border-focus);
-}
-
-.setting-input::placeholder {
- color: var(--text-disabled);
-}
-
-/* ── 按钮 (WinUI Button 风格) ── */
-.btn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 6px 16px;
- border: 1px solid transparent;
- border-radius: var(--radius-control);
- font-size: 13px;
- font-weight: 400;
- font-family: var(--font);
- cursor: pointer;
- transition: var(--transition);
- gap: 6px;
- position: relative;
- outline: none;
-}
-
-.btn::before {
- content: '';
- position: absolute;
- inset: 0;
- border-radius: inherit;
- background: currentColor;
- opacity: 0;
- transition: var(--transition);
-}
-
-.btn:hover::before { opacity: 0.06; }
-.btn:active::before { opacity: 0.04; }
-
-.btn:active {
- transform: scale(0.98);
-}
-
-.btn-primary {
- background: var(--accent);
- color: #1a1a1a;
- border-color: transparent;
-}
-
-.btn-primary:hover {
- background: var(--accent-hover);
-}
-
-.btn-primary::before { display: none; }
-
-.btn-outline {
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- color: var(--text-secondary);
-}
-
-.btn-outline:hover {
- border-color: var(--border-strong);
- color: var(--text-primary);
-}
-
-.btn-danger {
- background: var(--critical-bg);
- border: 1px solid rgba(241, 112, 123, 0.15);
- color: var(--critical);
-}
-
-.btn-danger:hover {
- background: rgba(241, 112, 123, 0.1);
-}
-
-.btn-sm {
- padding: 4px 10px;
- font-size: 12px;
-}
-
-/* ── 连接信息 ── */
-.connection-info {
- display: flex;
- align-items: center;
- gap: 10px;
- margin-bottom: 8px;
-}
-
-.status-badge {
- padding: 3px 10px;
- border-radius: var(--radius-control);
- font-size: 12px;
- font-weight: 500;
-}
-
-.status-badge.success {
- background: var(--success-bg);
- color: var(--success);
- border: 1px solid rgba(108, 203, 95, 0.15);
-}
-
-.status-badge.error {
- background: var(--critical-bg);
- color: var(--critical);
- border: 1px solid rgba(241, 112, 123, 0.15);
-}
-
-.status-badge.warning {
- background: var(--caution-bg);
- color: var(--caution);
- border: 1px solid rgba(252, 225, 0, 0.15);
-}
-
-.server-version {
- font-size: 12px;
- color: var(--text-tertiary);
- font-family: var(--font-mono);
-}
-
-.cors-hint {
- padding: 10px;
- background: var(--caution-bg);
- border: 1px solid rgba(252, 225, 0, 0.1);
- border-radius: var(--radius-control);
- margin-top: 8px;
-}
-
-.cors-hint p {
- font-size: 12px;
- color: var(--caution);
- margin-bottom: 6px;
-}
-
-.cors-hint code {
- display: block;
- padding: 8px;
- background: var(--bg-solid);
- border-radius: 3px;
- font-family: var(--font-mono);
- font-size: 12px;
- color: var(--accent);
- user-select: all;
-}
-
-/* ── 运行中的模型 ── */
-.running-models {
- display: flex;
- flex-direction: column;
- gap: 6px;
- margin-bottom: 10px;
-}
-
-.running-model {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 8px 10px;
- background: var(--bg-layer);
- border-radius: var(--radius-control);
- border: 1px solid var(--border-subtle);
-}
-
-.running-model .model-name {
- flex: 1;
- font-size: 13px;
- font-weight: 500;
-}
-
-.running-model .model-vram {
- font-size: 12px;
- color: var(--text-tertiary);
- font-family: var(--font-mono);
-}
-
-.text-muted {
- color: var(--text-tertiary);
- font-size: 13px;
-}
-
-/* ── 历史记录模态框加宽 ── */
-#historyModal .modal {
- max-width: 680px;
- max-height: 88vh;
-}
-
-/* ── 历史记录 ── */
-.history-list {
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.history-search-wrap {
- position: relative;
- margin-bottom: 10px;
-}
-
-.history-search-input {
- width: 100%;
- padding: 8px 32px 8px 12px;
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-control);
- color: var(--text-primary);
- font-size: 13px;
- font-family: var(--font);
- outline: none;
- transition: var(--transition);
-}
-
-.history-search-input::placeholder {
- color: var(--text-disabled);
-}
-
-.history-search-input:focus {
- border-color: var(--border-focus);
- box-shadow: 0 0 0 1px var(--border-focus);
-}
-
-.history-search-clear {
- position: absolute;
- right: 6px;
- top: 50%;
- transform: translateY(-50%);
- background: none;
- border: none;
- color: var(--text-tertiary);
- cursor: pointer;
- font-size: 14px;
- padding: 4px;
- line-height: 1;
-}
-
-.history-search-clear:hover {
- color: var(--text-primary);
-}
-
-.history-item {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 10px;
- border-radius: var(--radius-control);
- transition: var(--transition);
-}
-
-.history-item:hover {
- background: var(--bg-layer);
-}
-
-.history-info {
- flex: 1;
- min-width: 0;
- cursor: pointer;
-}
-
-.history-title {
- font-size: 13px;
- font-weight: 500;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.history-meta {
- display: flex;
- gap: 10px;
- font-size: 11px;
- color: var(--text-tertiary);
- margin-top: 2px;
-}
-
-.history-actions {
- display: flex;
- gap: 2px;
- flex-shrink: 0;
-}
-
-/* ── 历史分页 ── */
-.history-pagination {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 0 4px;
- border-top: 1px solid var(--border-subtle);
- margin-top: 8px;
-}
-
-.page-info {
- font-size: 12px;
- color: var(--text-tertiary);
-}
-
-.page-buttons {
- display: flex;
- align-items: center;
- gap: 4px;
-}
-
-.page-btn {
- min-width: 30px;
- height: 30px;
- border: 1px solid var(--border-default);
- background: var(--bg-layer);
- color: var(--text-secondary);
- border-radius: var(--radius-control);
- font-size: 12px;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: var(--transition);
-}
-
-.page-btn:hover:not(.disabled):not(.active) {
- border-color: var(--border-strong);
- color: var(--text-primary);
-}
-
-.page-btn.active {
- background: var(--accent-subtle);
- border-color: var(--accent);
- color: var(--accent);
- font-weight: 600;
-}
-
-.page-btn.disabled {
- opacity: 0.3;
- cursor: not-allowed;
-}
-
-.page-ellipsis {
- color: var(--text-disabled);
- font-size: 12px;
- padding: 0 2px;
-}
-
-/* ═══ Lightbox ═══ */
-.lightbox-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.75);
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 300;
- cursor: zoom-out;
- animation: overlayIn 167ms ease;
-}
-
-.lightbox-img {
- max-width: 90vw;
- max-height: 90vh;
- border-radius: var(--radius-md);
- box-shadow: var(--shadow-dialog);
- object-fit: contain;
- cursor: default;
-}
-
-.lightbox-close {
- position: fixed;
- top: 16px;
- right: 20px;
- width: 36px;
- height: 36px;
- border: 1px solid var(--border-default);
- background: var(--bg-card);
- color: var(--text-primary);
- font-size: 16px;
- border-radius: var(--radius-control);
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: var(--transition);
- z-index: 301;
-}
-
-.lightbox-close:hover {
- background: var(--bg-card-hover);
-}
-
-/* ═══ 帮助弹框 ═══ */
-#helpModal .modal {
- max-width: 600px;
- max-height: 85vh;
-}
-
-.help-section {
- margin-bottom: 16px;
- padding: 12px 14px;
- background: var(--bg-layer);
- border-radius: var(--radius-control);
- border-left: 2px solid var(--border-strong);
- transition: border-color var(--transition);
-}
-
-.help-section:hover {
- border-left-color: var(--accent);
-}
-
-.help-section:last-child {
- margin-bottom: 0;
-}
-
-.help-section h4 {
- font-size: 14px;
- font-weight: 600;
- margin-bottom: 8px;
- color: var(--text-primary);
-}
-
-.help-section ul,
-.help-section ol {
- padding-left: 18px;
- font-size: 13px;
- color: var(--text-secondary);
- line-height: 1.7;
-}
-
-.help-section li {
- margin-bottom: 3px;
-}
-
-.help-section code {
- background: rgba(255, 255, 255, 0.06);
- color: var(--accent);
- padding: 1px 6px;
- border-radius: 3px;
- font-size: 12px;
-}
-
-.help-section kbd {
- background: var(--bg-layer);
- border: 1px solid var(--border-default);
- border-bottom-width: 2px;
- border-radius: 3px;
- padding: 1px 6px;
- font-size: 12px;
- font-family: inherit;
- color: var(--text-primary);
-}
-
-.help-shortcuts {
- width: 100%;
- font-size: 13px;
- border-collapse: collapse;
- border-radius: var(--radius-control);
- overflow: hidden;
- border: 1px solid var(--border-default);
-}
-
-.help-shortcuts td {
- padding: 6px 10px;
- border-bottom: 1px solid var(--border-subtle);
-}
-
-.help-shortcuts tr:last-child td {
- border-bottom: none;
-}
-
-.help-shortcuts td:first-child {
- width: 140px;
- text-align: center;
- background: var(--bg-layer);
-}
-
-.help-shortcuts tr:hover td {
- background: var(--bg-layer-alt);
-}
-
-/* ═══ 知识库管理 ═══ */
-#kbModal .modal {
- max-width: 95vw;
- width: 1100px;
- max-height: 88vh;
-}
-
-#kbModal .modal-body {
- padding: 16px 20px;
- overflow: hidden;
- display: flex;
- flex-direction: column;
-}
-
-.kb-layout {
- display: flex;
- gap: 16px;
- flex: 1;
- min-height: 0;
- overflow: hidden;
-}
-
-.kb-sidebar {
- width: 240px;
- flex-shrink: 0;
- display: flex;
- flex-direction: column;
- gap: 8px;
- min-height: 0;
- overflow: hidden;
- padding-right: 16px;
- border-right: 1px solid var(--border-subtle);
-}
-
-.kb-new-collection {
- display: flex;
- gap: 6px;
-}
-
-.kb-new-collection .setting-input {
- flex: 1;
- margin-bottom: 0;
- font-size: 13px;
- padding: 6px 10px;
-}
-
-.kb-collection-list {
- flex: 1;
- overflow-y: auto;
- display: flex;
- flex-direction: column;
- gap: 2px;
- min-height: 0;
-}
-
-.kb-collection-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 12px;
- border-radius: var(--radius-control);
- cursor: pointer;
- transition: var(--transition);
-}
-
-.kb-collection-item:hover {
- background: var(--bg-layer);
-}
-
-.kb-collection-item.active {
- background: var(--accent-subtle);
- border: 1px solid rgba(96, 205, 255, 0.12);
-}
-
-.kb-col-info {
- display: flex;
- flex-direction: column;
- gap: 2px;
- min-width: 0;
-}
-
-.kb-col-name {
- font-size: 13px;
- font-weight: 500;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.kb-col-stats {
- font-size: 11px;
- color: var(--text-tertiary);
-}
-
-.kb-col-delete {
- opacity: 0;
- transition: opacity var(--transition);
-}
-
-.kb-collection-item:hover .kb-col-delete {
- opacity: 1;
-}
-
-.kb-main {
- flex: 1;
- display: flex;
- flex-direction: column;
- gap: 10px;
- min-width: 0;
- min-height: 0;
- overflow: hidden;
-}
-
-.kb-toolbar {
- display: flex;
- align-items: center;
- gap: 8px;
- flex-shrink: 0;
- flex-wrap: wrap;
- padding: 8px 12px;
- background: var(--bg-layer);
- border-radius: var(--radius-control);
- border: 1px solid var(--border-subtle);
-}
-
-.kb-embed-select {
- flex: 1;
- min-width: 0;
- max-width: 200px;
- padding: 5px 8px;
- font-size: 12px;
- background: var(--bg-layer-alt);
- color: var(--text-primary);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-control);
- outline: none;
- cursor: pointer;
- font-family: var(--font);
-}
-
-.kb-embed-select:focus {
- border-color: var(--border-focus);
-}
-
-.kb-rag-toggle {
- display: flex;
- align-items: center;
- gap: 6px;
- margin-left: auto;
- white-space: nowrap;
- padding-left: 8px;
- border-left: 1px solid var(--border-subtle);
-}
-
-.kb-progress {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- font-size: 12px;
- color: var(--text-tertiary);
- padding: 3px 8px;
- background: var(--accent-subtle);
- border-radius: var(--radius-control);
-}
-
-.kb-doc-list {
- flex: 1;
- overflow-y: auto;
- display: flex;
- flex-direction: column;
- gap: 2px;
- min-height: 0;
-}
-
-.kb-doc-item {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 12px;
- border-radius: var(--radius-control);
- transition: var(--transition);
-}
-
-.kb-doc-item:hover {
- background: var(--bg-layer);
-}
-
-.kb-doc-icon {
- font-size: 16px;
- flex-shrink: 0;
-}
-
-.kb-doc-info {
- flex: 1;
- display: flex;
- flex-direction: column;
- gap: 1px;
- min-width: 0;
-}
-
-.kb-doc-name {
- font-size: 13px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.kb-doc-meta {
- font-size: 11px;
- color: var(--text-tertiary);
-}
-
-.kb-doc-delete {
- opacity: 0;
- transition: opacity var(--transition);
-}
-
-.kb-doc-item:hover .kb-doc-delete {
- opacity: 1;
-}
-
-/* ── 移动端响应式 ── */
-@media (max-width: 640px) {
- .kb-layout {
- flex-direction: column;
- }
-
- .kb-sidebar {
- width: 100%;
- max-height: 160px;
- border-right: none;
- padding-right: 0;
- padding-bottom: 10px;
- border-bottom: 1px solid var(--border-subtle);
- }
-
- .kb-main {
- min-height: 180px;
- }
-
- .modal-lg {
- max-height: 92vh;
- }
-}
-
-/* ═══ Toast 通知 ═══ */
-.toast-container {
- position: fixed;
- top: 56px;
- right: 16px;
- z-index: 200;
- display: flex;
- flex-direction: column;
- gap: 6px;
- pointer-events: none;
-}
-
-.toast {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 10px 14px;
- border-radius: var(--radius-control);
- background: var(--bg-card);
- border: 1px solid var(--border-default);
- box-shadow: var(--shadow-flyout);
- font-size: 13px;
- color: var(--text-primary);
- pointer-events: auto;
- animation: toastIn 167ms ease;
- max-width: 320px;
-}
-
-.toast.removing {
- animation: toastOut 167ms ease forwards;
-}
-
-.toast-icon {
- font-size: 14px;
- flex-shrink: 0;
-}
-
-.toast.success { border-left: 3px solid var(--success); }
-.toast.error { border-left: 3px solid var(--critical); }
-.toast.warning { border-left: 3px solid var(--caution); }
-.toast.info { border-left: 3px solid var(--accent); }
-
-@keyframes toastIn {
- from { opacity: 0; transform: translateX(24px); }
- to { opacity: 1; transform: translateX(0); }
-}
-
-@keyframes toastOut {
- from { opacity: 1; transform: translateX(0); }
- to { opacity: 0; transform: translateX(24px); }
-}
-
-/* ═══ 响应式 ═══ */
-@media (min-width: 768px) {
- .app-header { padding: 8px 24px; }
- .model-bar { padding: 8px 24px; }
- .chat-area { padding: 20px 24px; }
- .messages { max-width: 820px; }
- .input-area { padding: 10px 24px 16px; }
- .modal { max-width: 500px; }
-}
-
-@media (min-width: 1024px) {
- .messages { max-width: 900px; }
-}
-
-/* ── 选中文本 ── */
-::selection {
- background: rgba(96, 205, 255, 0.3);
-}
-
-/* ── 链接 ── */
-.msg-content a {
- color: var(--accent);
- text-decoration: none;
-}
-
-.msg-content a:hover {
- text-decoration: underline;
-}
-
-.msg-content .blocked-link {
- color: var(--critical);
- text-decoration: line-through;
- cursor: not-allowed;
- opacity: 0.7;
-}
-
-/* ── 列表 ── */
-.msg-content ul, .msg-content ol {
- padding-left: 20px;
- margin: 8px 0;
-}
-
-.msg-content li { margin-bottom: 3px; }
-
-/* ── 表格 ── */
-.msg-content table {
- width: 100%;
- border-collapse: collapse;
- margin: 8px 0;
- font-size: 13px;
-}
-
-.msg-content th, .msg-content td {
- padding: 6px 10px;
- border: 1px solid var(--border-default);
- text-align: left;
-}
-
-.msg-content th {
- background: var(--bg-layer);
- font-weight: 600;
-}
-
-/* ── 引用块 ── */
-.msg-content blockquote {
- border-left: 2px solid var(--border-strong);
- padding-left: 12px;
- margin: 8px 0;
- color: var(--text-secondary);
-}
diff --git a/electron/desktop-bridge.js b/electron/desktop-bridge.js
deleted file mode 100644
index 4fc0974..0000000
--- a/electron/desktop-bridge.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Metona Ollama Desktop - 渲染进程桥接模块
- *
- * 在 Web 应用启动前加载,将桌面能力注入到 window 对象
- * 提供优雅降级:Web 环境下所有 desktop API 返回 null/空函数
- */
-
-// Desktop API 由 preload.js 通过 contextBridge 注入
-// 这里做一层封装,提供统一的降级处理
-
-const desktop = window.metonaDesktop || null;
-
-const bridge = {
- /** 是否在 Electron 桌面环境中 */
- isDesktop: !!desktop,
-
- /** 应用信息 */
- async getInfo() {
- if (!desktop) return { platform: 'web', version: '1.0.0' };
- return desktop.info();
- },
-
- /** 打开原生文件选择对话框 */
- async openFile(filters) {
- if (!desktop) return null;
- return desktop.dialog.openFile(filters);
- },
-
- /** 打开原生保存对话框 */
- async saveFile(options) {
- if (!desktop) return null;
- return desktop.dialog.saveFile(options);
- },
-
- /** 读取文件内容 */
- async readFile(filePath) {
- if (!desktop) return { success: false, error: 'Not in desktop' };
- return desktop.fs.readFile(filePath);
- },
-
- /** 写入文件 */
- async writeFile(filePath, content) {
- if (!desktop) return { success: false, error: 'Not in desktop' };
- return desktop.fs.writeFile(filePath, content);
- },
-
- /** 发送系统通知 */
- notify(title, body) {
- if (desktop) {
- desktop.notify(title, body);
- } else if ('Notification' in window && Notification.permission === 'granted') {
- new Notification(title, { body });
- }
- },
-
- /** 最小化窗口 */
- minimize() {
- desktop?.window.minimize();
- },
-
- /** 最大化/还原 */
- maximize() {
- desktop?.window.maximize();
- },
-
- /** 关闭窗口 */
- close() {
- desktop?.window.close();
- },
-
- /** 打开外部链接 */
- openExternal(url) {
- if (desktop) {
- desktop.openExternal(url);
- } else {
- window.open(url, '_blank');
- }
- },
-
- /** 监听主进程菜单事件 */
- onMenuAction(callback) {
- desktop?.onMenuAction(callback);
- },
-
- /** 监听托盘事件 */
- onTrayAction(callback) {
- desktop?.onTrayAction(callback);
- },
-
- /** 注销监听 */
- off(channel) {
- desktop?.removeAllListeners(channel);
- }
-};
-
-// 暴露到全局
-window.__metonaBridge = bridge;
-
-// 打印环境信息
-if (bridge.isDesktop) {
- bridge.getInfo().then(info => {
- console.log('[Desktop] Metona Ollama Desktop');
- console.log('[Desktop] Platform:', info.platform, info.arch);
- console.log('[Desktop] Electron:', info.electronVersion);
- console.log('[Desktop] User Data:', info.userDataPath);
- });
-}
diff --git a/electron/desktop-integration.js b/electron/desktop-integration.js
deleted file mode 100644
index da8c773..0000000
--- a/electron/desktop-integration.js
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * Metona Ollama Desktop - 桌面端集成
- *
- * 在 DOM 加载后执行,为现有 Web 应用添加桌面特性:
- * 1. 主进程菜单/托盘事件响应
- * 2. 原生文件对话框替换浏览器 input[type=file]
- * 3. 桌面端快捷键增强
- * 4. 标题栏集成
- */
-
-(function () {
- 'use strict';
-
- const bridge = window.__metonaBridge;
- if (!bridge || !bridge.isDesktop) {
- // 非桌面环境,跳过
- return;
- }
-
- console.log('[Desktop Integration] 初始化桌面集成...');
-
- // ── 1. 菜单/托盘事件响应 ──
- bridge.onMenuAction((action) => {
- switch (action) {
- case 'new-chat':
- document.querySelector('#btnNewChat')?.click();
- break;
- case 'import':
- document.querySelector('#btnImportSessions')?.click();
- break;
- case 'export':
- document.querySelector('#btnExportAll')?.click();
- break;
- case 'help':
- document.querySelector('#btnHelp')?.click();
- break;
- }
- });
-
- bridge.onTrayAction((action) => {
- switch (action) {
- case 'new-chat':
- document.querySelector('#btnNewChat')?.click();
- break;
- }
- });
-
- // ── 2. 增强文件上传 - 使用原生文件对话框 ──
- function setupNativeFilePicker(btnId, inputId, fileFilters) {
- const btn = document.querySelector(btnId);
- const input = document.querySelector(inputId);
- if (!btn || !input) return;
-
- // 拦截点击事件,使用原生对话框
- btn.addEventListener('click', async (e) => {
- e.preventDefault();
- e.stopPropagation();
-
- const filePaths = await bridge.openFile(fileFilters);
- if (!filePaths || filePaths.length === 0) return;
-
- // 读取所有文件
- for (const filePath of filePaths) {
- const result = await bridge.readFile(filePath);
- if (result.success) {
- // 创建 File 对象注入到原有流程
- const blob = new Blob([result.content], { type: 'text/plain' });
- const file = new File([blob], result.name, { type: 'text/plain' });
-
- // 使用 DataTransfer 注入文件到 input,触发 change 事件
- const dt = new DataTransfer();
- dt.items.add(file);
- input.files = dt.files;
- input.dispatchEvent(new Event('change', { bubbles: true }));
- }
- }
- }, true); // capture phase, 优先于原有事件
- }
-
- // DOM 就绪后设置
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', setupEnhancements);
- } else {
- setupEnhancements();
- }
-
- function setupEnhancements() {
- // 代码/文本文件上传
- setupNativeFilePicker(
- '#btnAttachFile',
- '#textFileInput',
- [
- { name: '代码文件', extensions: ['py', 'js', 'ts', 'tsx', 'java', 'go', 'rs', 'cpp', 'c', 'h', 'rb', 'php', 'sh', 'swift', 'kt', 'lua'] },
- { name: '配置文件', extensions: ['json', 'yaml', 'yml', 'toml', 'xml', 'ini', 'conf'] },
- { name: '文档', extensions: ['txt', 'md', 'log', 'csv', 'html', 'css'] },
- { name: '所有文件', extensions: ['*'] }
- ]
- );
- }
-
- // ── 3. 标题更新 - 显示连接/模型状态 ──
- const origTitle = document.title;
- window.addEventListener('metona:model-changed', (e) => {
- if (e.detail?.model) {
- document.title = `${e.detail.model} - Metona Ollama`;
- } else {
- document.title = origTitle;
- }
- });
-
- // ── 4. 桌面端键盘快捷键 ──
- document.addEventListener('keydown', (e) => {
- // Ctrl+N 新建会话(与菜单同步)
- if (e.ctrlKey && e.key === 'n') {
- e.preventDefault();
- document.querySelector('#btnNewChat')?.click();
- }
- });
-
- console.log('[Desktop Integration] 桌面集成完成 ✓');
-})();
diff --git a/electron/main.js b/electron/main.js
deleted file mode 100644
index f5a1a1d..0000000
--- a/electron/main.js
+++ /dev/null
@@ -1,435 +0,0 @@
-/**
- * Metona Ollama Desktop - 主进程
- *
- * 职责:
- * 1. 应用生命周期管理
- * 2. BrowserWindow 创建与配置
- * 3. 原生菜单、系统托盘
- * 4. IPC 通信桥梁(文件对话框、通知、窗口控制)
- * 5. 单实例锁
- */
-
-const { app, BrowserWindow, ipcMain, dialog, Menu, Tray, nativeImage, shell, Notification } = require('electron');
-const path = require('path');
-const fs = require('fs');
-
-// ── 常量 ──
-const APP_NAME = 'Metona Ollama';
-const ICON_PATH = path.join(__dirname, '..', 'assets', 'icons', 'llama.png');
-const ICO_PATH = path.join(__dirname, '..', 'assets', 'icons', 'llama.ico');
-const IS_DEV = !app.isPackaged;
-
-let mainWindow = null;
-let tray = null;
-let isQuitting = false;
-
-// ── 单实例锁 ──
-const gotTheLock = app.requestSingleInstanceLock();
-if (!gotTheLock) {
- app.quit();
-} else {
- app.on('second-instance', () => {
- if (mainWindow) {
- if (mainWindow.isMinimized()) mainWindow.restore();
- mainWindow.focus();
- }
- });
-}
-
-// ── 创建主窗口 ──
-function createMainWindow() {
- // 读取上次窗口大小
- const userDataPath = app.getPath('userData');
- const configPath = path.join(userDataPath, 'window-state.json');
- let windowState = { width: 1200, height: 800, x: undefined, y: undefined, maximized: false };
-
- try {
- if (fs.existsSync(configPath)) {
- windowState = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
- }
- } catch { /* 使用默认值 */ }
-
- mainWindow = new BrowserWindow({
- width: windowState.width || 1200,
- height: windowState.height || 800,
- x: windowState.x,
- y: windowState.y,
- minWidth: 800,
- minHeight: 600,
- icon: process.platform === 'win32' ? ICO_PATH : ICON_PATH,
- title: APP_NAME,
- backgroundColor: '#0a0a1a',
- show: false, // 等 ready-to-show 再显示,避免闪烁
- autoHideMenuBar: true,
- menuBarVisible: false,
- webPreferences: {
- preload: path.join(__dirname, 'preload.js'),
- contextIsolation: true,
- nodeIntegration: false,
- sandbox: false,
- // 关闭同源策略,避免 Ollama CORS 问题
- webSecurity: false,
- allowRunningInsecureContent: false
- }
- });
-
- // 加载应用
- mainWindow.loadFile(path.join(__dirname, '..', 'index.html'));
-
- // 彻底隐藏菜单栏
- mainWindow.setMenuBarVisibility(false);
-
- // 窗口准备好后显示
- mainWindow.once('ready-to-show', () => {
- if (windowState.maximized) {
- mainWindow.maximize();
- }
- mainWindow.show();
- });
-
- // 保存窗口状态
- const saveWindowState = () => {
- if (!mainWindow || mainWindow.isMaximized() || mainWindow.isMinimized()) return;
- const bounds = mainWindow.getBounds();
- try {
- fs.writeFileSync(configPath, JSON.stringify(bounds));
- } catch { /* 忽略写入错误 */ }
- };
-
- mainWindow.on('resize', saveWindowState);
- mainWindow.on('move', saveWindowState);
-
- mainWindow.on('maximize', () => {
- try {
- const state = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
- state.maximized = true;
- fs.writeFileSync(configPath, JSON.stringify(state));
- } catch { /* 忽略 */ }
- });
-
- mainWindow.on('unmaximize', () => {
- try {
- const state = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
- state.maximized = false;
- fs.writeFileSync(configPath, JSON.stringify(state));
- } catch { /* 忽略 */ }
- });
-
- // 关闭窗口时:最小化到托盘而非退出
- mainWindow.on('close', (e) => {
- if (!isQuitting && tray) {
- e.preventDefault();
- mainWindow.hide();
- // 首次最小化到托盘时显示通知
- if (!global._trayHintShown) {
- global._trayHintShown = true;
- showNotification('Metona 已最小化到系统托盘', '点击托盘图标可重新打开');
- }
- }
- });
-
- mainWindow.on('closed', () => {
- mainWindow = null;
- });
-
- // 外部链接用系统浏览器打开
- mainWindow.webContents.setWindowOpenHandler(({ url }) => {
- if (url.startsWith('http://') || url.startsWith('https://')) {
- shell.openExternal(url);
- return { action: 'deny' };
- }
- return { action: 'allow' };
- });
-
- return mainWindow;
-}
-
-// ── 创建系统托盘 ──
-function createTray() {
- const icon = nativeImage.createFromPath(process.platform === 'win32' ? ICO_PATH : ICON_PATH);
- tray = new Tray(icon.resize({ width: 16, height: 16 }));
-
- const contextMenu = Menu.buildFromTemplate([
- {
- label: '打开 Metona',
- click: () => {
- if (mainWindow) {
- mainWindow.show();
- mainWindow.focus();
- }
- }
- },
- { type: 'separator' },
- {
- label: '新建会话',
- click: () => {
- if (mainWindow) {
- mainWindow.show();
- mainWindow.focus();
- mainWindow.webContents.send('tray-action', 'new-chat');
- }
- }
- },
- { type: 'separator' },
- {
- label: '退出',
- click: () => {
- isQuitting = true;
- app.quit();
- }
- }
- ]);
-
- tray.setToolTip(APP_NAME);
- tray.setContextMenu(contextMenu);
-
- // 双击托盘图标打开窗口
- tray.on('double-click', () => {
- if (mainWindow) {
- mainWindow.show();
- mainWindow.focus();
- }
- });
-}
-
-// ── 原生菜单 ──
-function createMenu() {
- const template = [
- {
- label: '文件',
- submenu: [
- {
- label: '新建会话',
- accelerator: 'Ctrl+N',
- click: () => mainWindow?.webContents.send('menu-action', 'new-chat')
- },
- { type: 'separator' },
- {
- label: '导入会话',
- click: () => mainWindow?.webContents.send('menu-action', 'import')
- },
- {
- label: '导出会话',
- accelerator: 'Ctrl+Shift+E',
- click: () => mainWindow?.webContents.send('menu-action', 'export')
- },
- { type: 'separator' },
- {
- label: '退出',
- accelerator: 'Ctrl+Q',
- click: () => { isQuitting = true; app.quit(); }
- }
- ]
- },
- {
- label: '编辑',
- submenu: [
- { role: 'undo', label: '撤销' },
- { role: 'redo', label: '重做' },
- { type: 'separator' },
- { role: 'cut', label: '剪切' },
- { role: 'copy', label: '复制' },
- { role: 'paste', label: '粘贴' },
- { role: 'selectAll', label: '全选' }
- ]
- },
- {
- label: '视图',
- submenu: [
- { role: 'reload', label: '刷新' },
- { role: 'forceReload', label: '强制刷新' },
- { type: 'separator' },
- { role: 'zoomIn', label: '放大' },
- { role: 'zoomOut', label: '缩小' },
- { role: 'resetZoom', label: '重置缩放' },
- { type: 'separator' },
- { role: 'togglefullscreen', label: '全屏' }
- ]
- },
- {
- label: '窗口',
- submenu: [
- { role: 'minimize', label: '最小化' },
- {
- label: '最大化/还原',
- click: () => {
- if (mainWindow?.isMaximized()) mainWindow.unmaximize();
- else mainWindow?.maximize();
- }
- },
- { type: 'separator' },
- {
- label: '置顶',
- type: 'checkbox',
- checked: false,
- click: (menuItem) => {
- mainWindow?.setAlwaysOnTop(menuItem.checked);
- }
- }
- ]
- },
- {
- label: '帮助',
- submenu: [
- {
- label: '使用帮助',
- click: () => mainWindow?.webContents.send('menu-action', 'help')
- },
- { type: 'separator' },
- {
- label: '关于 Ollama',
- click: () => shell.openExternal('https://ollama.com')
- },
- {
- label: '关于 Metona',
- click: () => {
- dialog.showMessageBox(mainWindow, {
- type: 'info',
- title: '关于 Metona Ollama',
- message: 'Metona Ollama Desktop v1.1.0',
- detail: '基于原生 JavaScript 的 Ollama AI 聊天客户端\n桌面版由 Electron 驱动\n\nhttps://gitee.com/thzxx/metona-ollama',
- icon: process.platform === 'win32' ? ICO_PATH : ICON_PATH
- });
- }
- }
- ]
- }
- ];
-
- // 开发环境增加开发者工具菜单
- if (IS_DEV) {
- template.push({
- label: 'Dev',
- submenu: [
- {
- label: '切换开发者工具',
- accelerator: 'F12',
- click: () => mainWindow?.webContents.toggleDevTools()
- }
- ]
- });
- }
-
- const menu = Menu.buildFromTemplate(template);
- Menu.setApplicationMenu(menu);
-}
-
-// ── 系统通知 ──
-function showNotification(title, body) {
- if (Notification.isSupported()) {
- new Notification({ title, body, icon: ICON_PATH }).show();
- }
-}
-
-// ── IPC 处理 ──
-function setupIPC() {
- // 打开文件对话框
- ipcMain.handle('dialog:openFile', async (_, options) => {
- const result = await dialog.showOpenDialog(mainWindow, {
- properties: ['openFile', 'multiSelections'],
- filters: options?.filters || [
- { name: '所有文件', extensions: ['*'] }
- ]
- });
- if (result.canceled) return null;
- return result.filePaths;
- });
-
- // 保存文件对话框
- ipcMain.handle('dialog:saveFile', async (_, options) => {
- const result = await dialog.showSaveDialog(mainWindow, {
- defaultPath: options?.defaultPath || 'export',
- filters: options?.filters || [
- { name: '所有文件', extensions: ['*'] }
- ]
- });
- if (result.canceled) return null;
- return result.filePath;
- });
-
- // 读取文件内容
- ipcMain.handle('fs:readFile', async (_, filePath) => {
- try {
- const content = await fs.promises.readFile(filePath, 'utf-8');
- return { success: true, content, name: path.basename(filePath) };
- } catch (err) {
- return { success: false, error: err.message };
- }
- });
-
- // 写入文件
- ipcMain.handle('fs:writeFile', async (_, filePath, content) => {
- try {
- await fs.promises.writeFile(filePath, content, 'utf-8');
- return { success: true };
- } catch (err) {
- return { success: false, error: err.message };
- }
- });
-
- // 发送系统通知
- ipcMain.handle('notify', (_, title, body) => {
- showNotification(title, body);
- });
-
- // 获取应用信息
- ipcMain.handle('app:info', () => ({
- version: app.getVersion(),
- platform: process.platform,
- arch: process.arch,
- electronVersion: process.versions.electron,
- userDataPath: app.getPath('userData')
- }));
-
- // 窗口控制
- ipcMain.handle('window:minimize', () => mainWindow?.minimize());
- ipcMain.handle('window:maximize', () => {
- if (mainWindow?.isMaximized()) mainWindow.unmaximize();
- else mainWindow?.maximize();
- });
- ipcMain.handle('window:close', () => mainWindow?.close());
-
- // 打开外部链接
- ipcMain.handle('shell:openExternal', (_, url) => {
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('mailto:')) {
- shell.openExternal(url);
- }
- });
-}
-
-// ── 应用生命周期 ──
-app.whenReady().then(() => {
- setupIPC();
- createMainWindow();
- createTray();
- createMenu();
-
- // macOS: 点击 Dock 图标时重新创建窗口
- app.on('activate', () => {
- if (BrowserWindow.getAllWindows().length === 0) {
- createMainWindow();
- } else if (mainWindow) {
- mainWindow.show();
- }
- });
-});
-
-// 所有窗口关闭时不退出(有托盘)
-app.on('window-all-closed', () => {
- // macOS 习惯保留
- if (process.platform !== 'darwin') {
- // 有托盘时不退出
- if (!tray) app.quit();
- }
-});
-
-app.on('before-quit', () => {
- isQuitting = true;
-});
-
-// 移除默认菜单(Windows 生产环境)
-if (process.platform === 'win32' && !IS_DEV) {
- app.on('browser-window-created', (_, win) => {
- // 保留自定义菜单
- });
-}
diff --git a/electron/preload.js b/electron/preload.js
deleted file mode 100644
index a69c8ea..0000000
--- a/electron/preload.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Metona Ollama Desktop - Preload 脚本
- *
- * 通过 contextBridge 安全暴露 IPC 接口给渲染进程
- * 所有 API 都是白名单模式,不暴露 Node.js 全局对象
- */
-
-const { contextBridge, ipcRenderer } = require('electron');
-
-// ── 暴露 Desktop API ──
-contextBridge.exposeInMainWorld('metonaDesktop', {
- // 是否在桌面环境中
- isDesktop: true,
-
- // 版本信息
- info: () => ipcRenderer.invoke('app:info'),
-
- // ── 文件对话框 ──
- dialog: {
- openFile: (filters) => ipcRenderer.invoke('dialog:openFile', { filters }),
- saveFile: (options) => ipcRenderer.invoke('dialog:saveFile', options)
- },
-
- // ── 文件系统 ──
- fs: {
- readFile: (filePath) => ipcRenderer.invoke('fs:readFile', filePath),
- writeFile: (filePath, content) => ipcRenderer.invoke('fs:writeFile', filePath, content)
- },
-
- // ── 系统通知 ──
- notify: (title, body) => ipcRenderer.invoke('notify', title, body),
-
- // ── 窗口控制 ──
- window: {
- minimize: () => ipcRenderer.invoke('window:minimize'),
- maximize: () => ipcRenderer.invoke('window:maximize'),
- close: () => ipcRenderer.invoke('window:close')
- },
-
- // ── 外部链接 ──
- openExternal: (url) => ipcRenderer.invoke('shell:openExternal', url),
-
- // ── 主进程事件监听 ──
- onMenuAction: (callback) => {
- ipcRenderer.on('menu-action', (_, action) => callback(action));
- },
- onTrayAction: (callback) => {
- ipcRenderer.on('tray-action', (_, action) => callback(action));
- },
- // 移除监听
- removeAllListeners: (channel) => {
- ipcRenderer.removeAllListeners(channel);
- }
-});
diff --git a/index.html b/index.html
deleted file mode 100644
index f889961..0000000
--- a/index.html
+++ /dev/null
@@ -1,456 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Metona Ollama
-
-
-
-
-
-
-
-
-
-
-
-
-
- 🧠 Think
- 👁️ Vision
- 📚 RAG
-
-
-
-
-
-
-
-
开始对话
-
选择一个模型,输入消息开始聊天
-
-
-
-
-
-
- 回到底部
-
-
-
-
-
- 📖 正在查看历史记录(只读模式)
- 返回新会话
-
-
-
-
-
-
-
-
-
-
-
-
-
Ollama 服务地址
-
-
- 检测中...
-
-
-
-
⚠️ 连接失败,可能是 CORS 问题。请在启动 Ollama 时设置环境变量:
-
OLLAMA_ORIGINS="*" ollama serve
-
-
-
-
- 系统提示词(System Prompt)
-
-
-
-
-
-
-
上下文长度(num_ctx)
-
-
- tokens
-
-
常见值:2048 / 4096 / 8192 / 32768。值越大上下文越长,显存占用越高。
-
温度(Temperature):0.7
-
-
低值更精确稳定,高值更有创意多样(0 = 确定性,2 = 最大随机)
-
-
-
- 显存管理
- 释放显存(卸载模型)
-
-
-
数据管理
-
- 📦 导出全部会话
- 📥 导入会话
-
- 清空所有历史
-
-
导出格式为 JSON,可备份后在其他设备导入恢复。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
🚀 快速开始
-
- 确保 Ollama 已启动(默认 http://127.0.0.1:11434)
- 顶部下拉框选择一个模型
- 输入消息,按 Enter 发送,Shift+Enter 换行
-
-
-
-
💬 聊天功能
-
- 流式回复 — 实时打字效果,随时点 ■ 停止
- Think 推理 — 🧠 按钮开启深度思考(需模型支持)
- 上下文长度 — 设置中调整 num_ctx,越大记忆越长
- 系统提示词 — 设置中开启,定义 AI 的角色和行为
-
-
-
-
📎 文件与图片
-
- 📷 上传图片 — 图标按钮上传,模型需支持 Vision
- 📄 上传文件 — 支持 50+ 种文本/代码格式,单文件 ≤500KB
- 文件内容自动以代码块格式发送给模型分析
-
-
-
-
📚 知识库 (RAG)
-
- 点击顶部 📚 按钮打开知识库管理
- 创建集合 → 选择嵌入模型(推荐 nomic-embed-text 等)
- 上传文档,自动分块并生成向量索引
- 开启 RAG 检索后,聊天时自动检索相关文档注入回答
-
-
-
-
🤖 Agent 预设
-
- 模型栏下方显示预设栏 ,点击胶囊按钮一键切换角色/工作模式
- 内置预设 — 💬默认、🌐翻译官、🔍代码审查、✍️写作助手、📊数据分析师、🎓学习导师
- 切换预设自动应用:系统提示词、温度、上下文长度、Think 开关
- 点击 + 按钮可创建自定义预设(名称、图标、系统提示词等全部可配)
- 右键自定义预设胶囊可编辑/删除
-
-
-
-
🕐 历史记录
-
- 所有会话自动保存到浏览器本地(IndexedDB)
- 点击 🕐 按钮查看、搜索、恢复历史会话
- 支持导出 .metona 加密备份文件
-
-
-
-
⚙️ 设置
-
- 服务地址 — 修改 Ollama API 端点
- 系统提示词 — 开启后定义 AI 的角色和行为
- 上下文长度 — 调整 num_ctx,值越大记忆越长
- 温度 — 调节回答随机性(0 精确稳定 ~ 2 最大创意)
- 释放显存 — 卸载当前模型,释放 GPU 内存
- 跨域问题 — 启动 Ollama 时设置 OLLAMA_ORIGINS="*"
-
-
-
-
⚡ 快捷键
-
- Enter 发送消息
- Shift + Enter 换行
- Esc 关闭弹窗
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 系统提示词(System Prompt)
-
-
-
-
温度(Temperature):0.7
-
-
低值更精确稳定,高值更有创意多样(0 = 确定性,2 = 最大随机)
-
-
-
上下文长度(num_ctx)
-
-
- tokens
-
-
-
- Think 深度推理
-
-
-
- 需要模型支持
-
-
-
-
-
-
-
-
-
-
✕
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/js/app.js b/js/app.js
deleted file mode 100644
index de4d320..0000000
--- a/js/app.js
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * App - 主入口模块
- * 负责初始化所有组件、协调事件流、管理生命周期
- */
-
-import { ChatDB } from './chat-db.js';
-import { OllamaAPI } from './ollama-api.js';
-import { state, KEYS } from './state.js';
-import { generateId } from './utils.js';
-
-// 组件
-import { initToast, showToast } from './components/toast.js';
-import { initLightbox, closeLightbox } from './components/lightbox.js';
-import { initHeader, checkConnection } from './components/header.js';
-import { initModelBar, loadModels, setSelectedModel } from './components/model-bar.js';
-import { initChatArea, renderMessages, clearMessages, enableAutoScroll } from './components/chat-area.js';
-import { initInputArea } from './components/input-area.js';
-import { initSettingsModal, closeSettingsModal } from './components/settings-modal.js';
-import { initHistoryModal, closeHistoryModal } from './components/history-modal.js';
-import { initKBModal } from './components/kb-modal.js';
-import { initPresetBar, initPresetModal } from './components/preset-bar.js';
-import { initPresetManager } from './preset-manager.js';
-import { initVectorStore } from './rag.js';
-
-function initHelpModal() {
- const helpModal = document.querySelector('#helpModal');
- document.querySelector('#btnHelp').addEventListener('click', () => {
- helpModal.style.display = '';
- });
- document.querySelector('#btnCloseHelp').addEventListener('click', () => {
- helpModal.style.display = 'none';
- });
- helpModal.addEventListener('click', (e) => {
- if (e.target === helpModal) helpModal.style.display = 'none';
- });
-}
-
-console.log('[Metona] 模块化版本 v4.0.3 ' + new Date().toISOString());
-
-// ── 会话管理 ──
-
-function createNewSession() {
- const selectedModel = document.querySelector('#modelSelect')?.value || '';
- const defaultModel = selectedModel || state.get('_defaultModel', '');
- return {
- id: generateId(),
- title: '新会话',
- model: defaultModel,
- messages: [],
- createdAt: Date.now(),
- updatedAt: Date.now()
- };
-}
-
-async function startNewSession() {
- // 先保存当前会话(如果有的话)
- const db = state.get(KEYS.DB);
- const currentSession = state.get(KEYS.CURRENT_SESSION);
- if (db && currentSession && currentSession.messages.length > 0) {
- currentSession.updatedAt = Date.now();
- await db.saveSession(currentSession);
- }
-
- // 释放显存,清理模型上下文
- const api = state.get(KEYS.API);
- const model = currentSession?.model || state.get('_defaultModel', '');
- if (api && model) {
- try {
- await api.chat({ model, messages: [], keep_alive: 0 });
- } catch (e) {
- console.warn('[App] 释放显存失败:', e);
- }
- }
-
- const session = createNewSession();
- state.set(KEYS.CURRENT_SESSION, session);
- state.set(KEYS.IS_HISTORY_VIEW, false);
-
- document.querySelector('#historyBar').style.display = 'none';
- document.querySelector('#inputArea').style.display = '';
-
- clearMessages();
- enableAutoScroll();
- renderMessages();
- document.querySelector('#chatInput')?.focus();
-}
-
-// ── 初始化 ──
-
-async function init() {
- let db, api;
-
- try {
- // 1. 初始化 IndexedDB
- db = new ChatDB();
- await db.init();
- state.set(KEYS.DB, db);
-
- // 2. 读取保存的设置
- const serverUrl = await db.getSetting('serverUrl', 'http://127.0.0.1:11434');
- api = new OllamaAPI(serverUrl);
- state.set(KEYS.API, api);
-
- // 3. 创建初始会话
- state.set(KEYS.CURRENT_SESSION, createNewSession());
- state.set(KEYS.IS_STREAMING, false);
- state.set(KEYS.IS_HISTORY_VIEW, false);
- state.set(KEYS.NUM_CTX, 24576);
-
- // 4. 初始化所有组件
- initToast();
- initLightbox();
- initHeader();
- initModelBar();
- initChatArea();
- initInputArea();
- initSettingsModal();
- initHistoryModal();
- initKBModal();
- initPresetBar();
- initPresetModal();
- initHelpModal();
-
- // 5. 绑定全局事件
- bindGlobalEvents();
-
- // 6. 检测连接 + 加载模型
- await checkConnection();
- await loadModels();
-
- // 6.1 初始化向量存储
- await initVectorStore();
-
- // 6.2 初始化预设管理器(会从 DB 加载并应用上次激活的预设)
- await initPresetManager();
-
- // 7. 恢复上次选择的模型
- const savedModel = await db.getSetting('selectedModel', '');
- if (savedModel) {
- setSelectedModel(savedModel);
- state.set('_defaultModel', savedModel);
- await db.saveSetting('selectedModel', savedModel);
- }
-
- // 8. 恢复系统提示词、上下文长度、温度
- const systemPromptEnabled = await db.getSetting('systemPromptEnabled', false);
- const systemPrompt = await db.getSetting('systemPrompt', '');
- const numCtx = await db.getSetting('numCtx', 24576);
- const temperature = await db.getSetting('temperature', 0.7);
-
- state.set(KEYS.SYSTEM_PROMPT_ENABLED, systemPromptEnabled);
- state.set(KEYS.SYSTEM_PROMPT, systemPrompt);
- state.set(KEYS.NUM_CTX, numCtx);
- state.set('temperature', temperature);
-
- document.querySelector('#toggleSystemPrompt').checked = systemPromptEnabled;
- document.querySelector('#inputSystemPrompt').disabled = !systemPromptEnabled;
- document.querySelector('#inputSystemPrompt').value = systemPrompt;
- document.querySelector('#inputNumCtx').value = numCtx;
- document.querySelector('#inputTemperature').value = temperature;
- document.querySelector('#tempValue').textContent = parseFloat(temperature).toFixed(1);
-
- // 9. 聚焦输入框
- document.querySelector('#chatInput')?.focus();
-
- // 10. 注册 Service Worker (PWA)
- initServiceWorker();
-
- console.log('[App] 初始化完成');
- } catch (err) {
- console.error('[App] 初始化失败:', err);
- // 即使 DB 失败也尝试运行
- if (!api) {
- api = new OllamaAPI();
- state.set(KEYS.API, api);
- }
- state.set(KEYS.CURRENT_SESSION, createNewSession());
- bindGlobalEvents();
- initToast();
- initLightbox();
- initHeader();
- initModelBar();
- initChatArea();
- initInputArea();
- initSettingsModal();
- initHistoryModal();
- initPresetBar();
- initPresetModal();
- initPresetManager();
- checkConnection().then(loadModels);
- }
-}
-
-function bindGlobalEvents() {
- // 新建会话
- document.querySelector('#btnNewChat')?.addEventListener('click', startNewSession);
-
- // ESC 关闭模态框
- document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape') {
- closeSettingsModal();
- closeHistoryModal();
- closeLightbox();
- document.querySelector('#helpModal').style.display = 'none';
- document.querySelector('#kbModal').style.display = 'none';
- }
- });
-
- // 全局错误捕获
- window.addEventListener('error', (e) => {
- console.error('[App] 未捕获错误:', e.error || e.message);
- showToast(`发生错误: ${e.message}`, 'error', 5000);
- });
-
- window.addEventListener('unhandledrejection', (e) => {
- console.error('[App] 未处理 Promise 拒绝:', e.reason);
- const msg = e.reason?.message || String(e.reason);
- showToast(`操作失败: ${msg}`, 'error', 5000);
- });
-}
-
-function initServiceWorker() {
- if (!('serviceWorker' in navigator)) return;
-
- navigator.serviceWorker.getRegistrations()
- .then(regs => regs.forEach(r => r.unregister()))
- .then(() => caches.keys().then(keys => Promise.all(keys.map(k => caches.delete(k)))))
- .then(() => navigator.serviceWorker.register('sw.js?v=3', { updateViaCache: 'none' }))
- .then(reg => {
- reg.addEventListener('updatefound', () => {
- const w = reg.installing;
- if (w) w.addEventListener('statechange', () => {
- if (w.state === 'activated') window.location.reload();
- });
- });
- reg.update();
- })
- .catch(err => console.warn('[App] Service Worker 注册失败:', err));
-}
-
-// 启动
-init();
diff --git a/js/chat-db.js b/js/chat-db.js
deleted file mode 100644
index 53ddb3b..0000000
--- a/js/chat-db.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/**
- * ChatDB - IndexedDB 封装层
- *
- * 用于持久化聊天历史记录和设置
- * 使用 IndexedDB 而非 localStorage,原因:
- * 1. 支持存储更大的数据(如 base64 图片)
- * 2. 异步操作不阻塞主线程
- * 3. 支持事务和索引查询
- */
-
-export class ChatDB {
- constructor(dbName = 'metona-ollama', version = 1) {
- this.dbName = dbName;
- this.version = version;
- this.db = null;
- }
-
- /**
- * 初始化数据库
- * 创建 object stores 和索引
- */
- async init() {
- return new Promise((resolve, reject) => {
- const request = indexedDB.open(this.dbName, this.version);
-
- request.onerror = () => {
- console.error('[ChatDB] 数据库打开失败:', request.error);
- reject(request.error);
- };
-
- request.onsuccess = () => {
- this.db = request.result;
- console.log('[ChatDB] 数据库已连接');
- resolve();
- };
-
- // 数据库升级/创建回调
- request.onupgradeneeded = (event) => {
- const db = event.target.result;
-
- // 会话存储
- if (!db.objectStoreNames.contains('sessions')) {
- const sessionStore = db.createObjectStore('sessions', { keyPath: 'id' });
- sessionStore.createIndex('updatedAt', 'updatedAt', { unique: false });
- sessionStore.createIndex('model', 'model', { unique: false });
- console.log('[ChatDB] 创建 sessions 存储');
- }
-
- // 设置存储
- if (!db.objectStoreNames.contains('settings')) {
- db.createObjectStore('settings', { keyPath: 'key' });
- console.log('[ChatDB] 创建 settings 存储');
- }
- };
- });
- }
-
- /**
- * 确保数据库已连接
- */
- _ensureDB() {
- if (!this.db) throw new Error('数据库未初始化,请先调用 init()');
- }
-
- /**
- * 获取事务
- */
- _tx(storeName, mode = 'readonly') {
- this._ensureDB();
- const tx = this.db.transaction(storeName, mode);
- return tx.objectStore(storeName);
- }
-
- // ═══════════════════════════════════════════════════════════════
- // 会话 CRUD
- // ═══════════════════════════════════════════════════════════════
-
- /** 保存/更新会话 */
- async saveSession(session) {
- return new Promise((resolve, reject) => {
- const store = this._tx('sessions', 'readwrite');
- const request = store.put(session);
- request.onsuccess = () => resolve(session.id);
- request.onerror = () => reject(request.error);
- });
- }
-
- /** 获取单个会话 */
- async getSession(id) {
- return new Promise((resolve, reject) => {
- const store = this._tx('sessions');
- const request = store.get(id);
- request.onsuccess = () => resolve(request.result || null);
- request.onerror = () => reject(request.error);
- });
- }
-
- /** 获取所有会话 */
- async getAllSessions() {
- return new Promise((resolve, reject) => {
- const store = this._tx('sessions');
- const request = store.getAll();
- request.onsuccess = () => resolve(request.result || []);
- request.onerror = () => reject(request.error);
- });
- }
-
- /** 删除会话 */
- async deleteSession(id) {
- return new Promise((resolve, reject) => {
- const store = this._tx('sessions', 'readwrite');
- const request = store.delete(id);
- request.onsuccess = () => resolve();
- request.onerror = () => reject(request.error);
- });
- }
-
- /** 清空所有会话 */
- async clearAll() {
- return new Promise((resolve, reject) => {
- const store = this._tx('sessions', 'readwrite');
- const request = store.clear();
- request.onsuccess = () => resolve();
- request.onerror = () => reject(request.error);
- });
- }
-
- /** 批量保存会话(单事务,原子性) */
- async importSessions(sessions) {
- return new Promise((resolve, reject) => {
- const tx = this.db.transaction('sessions', 'readwrite');
- const store = tx.objectStore('sessions');
- let imported = 0;
- let skipped = 0;
-
- tx.oncomplete = () => resolve({ imported, skipped });
- tx.onerror = () => reject(tx.error);
-
- // 先检查每个 ID 是否已存在,再 put
- for (const session of sessions) {
- if (!session.id || !Array.isArray(session.messages)) {
- skipped++;
- continue;
- }
- const getRequest = store.get(session.id);
- getRequest.onsuccess = () => {
- if (getRequest.result) {
- skipped++;
- } else {
- store.put(session);
- imported++;
- }
- };
- }
- });
- }
-
- /** 按时间范围查询会话 */
- async getSessionsByTimeRange(startTime, endTime) {
- return new Promise((resolve, reject) => {
- const store = this._tx('sessions');
- const index = store.index('updatedAt');
- const range = IDBKeyRange.bound(startTime, endTime);
- const request = index.getAll(range);
- request.onsuccess = () => resolve(request.result || []);
- request.onerror = () => reject(request.error);
- });
- }
-
- // ═══════════════════════════════════════════════════════════════
- // 设置 CRUD
- // ═══════════════════════════════════════════════════════════════
-
- /** 保存设置 */
- async saveSetting(key, value) {
- return new Promise((resolve, reject) => {
- const store = this._tx('settings', 'readwrite');
- const request = store.put({ key, value });
- request.onsuccess = () => resolve();
- request.onerror = () => reject(request.error);
- });
- }
-
- /** 获取设置 */
- async getSetting(key, defaultValue = null) {
- return new Promise((resolve, reject) => {
- const store = this._tx('settings');
- const request = store.get(key);
- request.onsuccess = () => resolve(request.result ? request.result.value : defaultValue);
- request.onerror = () => reject(request.error);
- });
- }
-}
diff --git a/js/components/chat-area.js b/js/components/chat-area.js
deleted file mode 100644
index 7dded6c..0000000
--- a/js/components/chat-area.js
+++ /dev/null
@@ -1,395 +0,0 @@
-/**
- * ChatArea - 聊天区域组件
- * 负责消息渲染、流式更新、导出功能
- */
-
-import { state, KEYS } from '../state.js';
-import { marked } from '../marked-config.js';
-import { escapeHtml, formatTime, downloadFile } from '../utils.js';
-
-/** 根据文件名返回合适的图标 */
-function getFileIcon(filename) {
- const name = filename.toLowerCase();
- if (name === 'dockerfile') return '🐳';
- if (name === 'makefile' || name === 'gnumakefile') return '🔨';
- const ext = filename.split('.').pop().toLowerCase();
- const map = {
- py: '🐍', pyw: '🐍', pyi: '🐍',
- js: '💛', mjs: '💛', cjs: '💛',
- ts: '🔷', tsx: '⚛️', jsx: '⚛️',
- java: '☕', go: '🐹', rs: '🦀', rb: '💎',
- php: '🐘', swift: '🍎', kt: '🤖', scala: '🔴',
- lua: '🌙', pl: '🐪', r: '📐', m: '📐',
- cs: '🟪', fs: '🔵', vb: '🔷',
- ex: '💧', exs: '💧', erl: '📞', hs: '🟣',
- clj: '🟢', lisp: '🟢',
- cpp: '⚙️', cc: '⚙️', cxx: '⚙️', hpp: '⚙️',
- c: '⚙️', h: '⚙️',
- sh: '🐚', bash: '🐚', zsh: '🐚', fish: '🐚',
- html: '🌐', htm: '🌐', css: '🎨', svg: '🎨',
- json: '📋', yaml: '📋', yml: '📋', toml: '📋',
- ini: '📋', cfg: '📋', conf: '📋',
- xml: '📰', sql: '🗃️',
- md: '📝', markdown: '📝', rst: '📝', txt: '📄',
- log: '📜', csv: '📊', tsv: '📊',
- diff: '🔀', patch: '🔀',
- };
- return map[ext] || '📄';
-}
-
-let chatAreaEl, messagesContainerEl, emptyStateEl, scrollBtnEl;
-let autoScroll = true; // 是否自动滚动到底部
-let currentPlaceholder = null; // 当前流式消息的 placeholder 引用
-
-export function initChatArea() {
- chatAreaEl = document.querySelector('#chatArea');
- messagesContainerEl = document.querySelector('#messagesContainer');
- emptyStateEl = document.querySelector('#emptyState');
- scrollBtnEl = document.querySelector('#scrollToBottom');
-
- // 监听用户手动滚动
- chatAreaEl.addEventListener('scroll', () => {
- const atBottom = isAtBottom();
- if (atBottom) {
- autoScroll = true;
- scrollBtnEl.style.display = 'none';
- } else {
- autoScroll = false;
- scrollBtnEl.style.display = '';
- }
- });
-
- // 点击滚动到底部按钮
- scrollBtnEl.addEventListener('click', () => {
- autoScroll = true;
- scrollBtnEl.style.display = 'none';
- chatAreaEl.scrollTo({ top: chatAreaEl.scrollHeight, behavior: 'smooth' });
- });
-}
-
-/** 判断是否在底部附近(60px 容差) */
-function isAtBottom() {
- return chatAreaEl.scrollHeight - chatAreaEl.scrollTop - chatAreaEl.clientHeight < 60;
-}
-
-/** 安全地将 Markdown 转为 HTML */
-export function safeMarkdown(text) {
- if (!text && text !== 0) return '';
- const str = typeof text === 'string' ? text : String(text);
- if (!str) return '';
- try {
- const result = marked.parse(str);
- return typeof result === 'string' ? result : escapeHtml(str);
- } catch (e) {
- return escapeHtml(str);
- }
-}
-
-/** 滚动到底部(仅当 autoScroll 开启时) */
-export function scrollToBottom() {
- if (!autoScroll) return;
- requestAnimationFrame(() => {
- chatAreaEl.scrollTop = chatAreaEl.scrollHeight;
- });
-}
-
-/** 渲染所有消息(增量渲染) */
-export function renderMessages() {
- const currentSession = state.get(KEYS.CURRENT_SESSION);
- const msgs = currentSession ? currentSession.messages : [];
-
- if (msgs.length === 0) {
- emptyStateEl.style.display = '';
- messagesContainerEl.style.display = 'none';
- return;
- }
-
- emptyStateEl.style.display = 'none';
- messagesContainerEl.style.display = '';
-
- const existingCount = messagesContainerEl.children.length;
- const isNewMessage = existingCount > 0 && existingCount < msgs.length;
- for (let i = existingCount; i < msgs.length; i++) {
- appendMessageDOM(msgs[i], i);
- }
-
- // 新消息按 autoScroll 决定;首次渲染(加载历史)强制滚到底部
- if (isNewMessage) {
- scrollToBottom();
- } else if (msgs.length > 0) {
- chatAreaEl.scrollTop = chatAreaEl.scrollHeight;
- }
-}
-
-/** 追加单条消息 DOM */
-export function appendMessageDOM(msg, index) {
- const div = document.createElement('div');
- div.className = `message ${msg.role}`;
- div.dataset.index = index;
-
- const avatar = msg.role === 'user' ? '👤' : '🤖';
- const roleLabel = msg.role === 'user' ? '你' : 'AI';
- const modelTag = (msg.role === 'assistant' && msg.model)
- ? `${escapeHtml(msg.model)} `
- : '';
-
- let contentHtml = '';
- const safeContent = (msg.content != null) ? String(msg.content) : '';
-
- if (msg.role === 'assistant') {
- // 思考块
- if (msg.think) {
- contentHtml += `
-
-
-
-
${escapeHtml(msg.think)}
-
-
`;
- }
- if (safeContent) {
- contentHtml += `${safeMarkdown(safeContent)}
`;
- } else {
- contentHtml += `
`;
- }
- } else {
- contentHtml += `${escapeHtml(safeContent)}
`;
- }
-
- // 图片
- if (msg.images && msg.images.length > 0) {
- contentHtml += '';
- for (const img of msg.images) {
- contentHtml += `
`;
- }
- contentHtml += '
';
- }
-
- // 文件 chip(展示用,不暴露文件内容)
- if (msg.files && msg.files.length > 0) {
- contentHtml += '';
- for (const f of msg.files) {
- const icon = getFileIcon(f.name);
- contentHtml += `
${icon} ${escapeHtml(f.name)}
`;
- }
- contentHtml += '
';
- }
-
- // 纯图片/文件消息隐藏空内容
- if (msg.role === 'user' && (!msg.content || msg.content.trim() === '') && (msg.images?.length > 0 || msg.files?.length > 0)) {
- contentHtml = contentHtml.replace('
', '');
- } else if (msg.role === 'user' && msg.files?.length > 0 && msg.content) {
- // 有文本也有文件,保留文本内容展示
- }
-
- // 统计信息
- const stats = [];
- if (msg.timestamp) stats.push(formatTime(msg.timestamp));
- if (msg.eval_count) stats.push(`${msg.eval_count} tokens`);
- if (msg.total_duration) stats.push(`${(msg.total_duration / 1e9).toFixed(1)}s`);
- if (stats.length > 0) {
- contentHtml += `${stats.map(s => `${s} `).join(' · ')}
`;
- }
-
- // RAG 知识库来源
- if (msg.role === 'assistant' && msg.ragSources && msg.ragSources.length > 0) {
- const sourcesHtml = msg.ragSources.map((s, i) => {
- const scorePercent = s.score ? `${(s.score * 100).toFixed(0)}%` : '';
- return `
- 📄 来源 ${i + 1}: ${escapeHtml(s.filename)}
- ${scorePercent ? `${scorePercent} ` : ''}
-
`;
- }).join('');
- contentHtml += `
-
- ${sourcesHtml}
-
`;
- }
-
- div.innerHTML = `
- ${avatar}
-
-
${roleLabel}${modelTag}
- ${contentHtml}
-
- `;
-
- messagesContainerEl.appendChild(div);
-}
-
-/** 更新最后一条 assistant 消息(流式输出) */
-export function updateLastAssistantMessage(content, think, stats, model) {
- // 使用 placeholder 引用,完全不依赖 CSS 选择器
- const lastMsg = currentPlaceholder;
- if (!lastMsg) return;
-
- // 仅首次调用时移除 loading 指示器
- if (lastMsg.classList.contains('loading')) {
- lastMsg.classList.remove('loading');
- const loadingDots = lastMsg.querySelector('.loading-dots');
- const loadingText = lastMsg.querySelector('.loading-text');
- if (loadingDots) loadingDots.remove();
- if (loadingText) loadingText.remove();
- }
-
- if (model) {
- let modelTag = lastMsg.querySelector('.model-tag');
- if (!modelTag) {
- modelTag = document.createElement('span');
- modelTag.className = 'model-tag';
- const roleEl = lastMsg.querySelector('.msg-role');
- if (roleEl) roleEl.appendChild(modelTag);
- }
- modelTag.textContent = model;
- }
-
- const contentDiv = lastMsg.querySelector('.msg-content');
- const safeContent = (content != null) ? String(content) : '';
- if (contentDiv && safeContent) {
- contentDiv.innerHTML = safeMarkdown(safeContent);
- }
-
- // 思考块
- let thinkBlock = lastMsg.querySelector('.think-block');
- if (think) {
- if (!thinkBlock) {
- const msgBody = lastMsg.querySelector('.msg-body');
- thinkBlock = document.createElement('div');
- thinkBlock.className = 'think-block';
- thinkBlock.innerHTML = `
-
-
- `;
- msgBody.insertBefore(thinkBlock, contentDiv);
- }
- thinkBlock.querySelector('pre').textContent = think;
- }
-
- if (stats) {
- let statsDiv = lastMsg.querySelector('.msg-stats');
- if (!statsDiv) {
- statsDiv = document.createElement('div');
- statsDiv.className = 'msg-stats';
- lastMsg.querySelector('.msg-body').appendChild(statsDiv);
- }
- const parts = [];
- if (stats.eval_count) parts.push(`${stats.eval_count} tokens`);
- if (stats.total_duration) parts.push(`${(stats.total_duration / 1e9).toFixed(1)}s`);
- if (parts.length) statsDiv.innerHTML = parts.join(' · ');
- }
-
- scrollToBottom();
-}
-
-/** 追加临时的 assistant 消息占位 */
-export function appendAssistantPlaceholder() {
- const div = document.createElement('div');
- div.className = 'message assistant loading';
- div.innerHTML = `
- 🤖
-
- `;
- messagesContainerEl.appendChild(div);
- currentPlaceholder = div;
- scrollToBottom();
-}
-
-/** 追加系统提示消息 */
-export function appendSystemMessage(text, tempClass) {
- const div = document.createElement('div');
- div.style.cssText = 'text-align:center;padding:8px;font-size:12px;color:var(--text-muted);';
- if (tempClass) div.classList.add(tempClass);
- div.textContent = text;
- emptyStateEl.style.display = 'none';
- messagesContainerEl.style.display = '';
- messagesContainerEl.appendChild(div);
- scrollToBottom();
-}
-
-/** 获取消息容器 */
-export function getMessagesContainer() {
- return messagesContainerEl;
-}
-
-/** 清空消息 DOM */
-export function clearMessages() {
- messagesContainerEl.innerHTML = '';
- currentPlaceholder = null;
-}
-
-/** 重置自动滚动(用户发送消息、新建会话时调用) */
-export function enableAutoScroll() {
- autoScroll = true;
- if (scrollBtnEl) scrollBtnEl.style.display = 'none';
-}
-
-// ── 导出功能 ──
-
-/** 导出为 Markdown */
-export function exportAsMarkdown(session) {
- let md = `# ${session.title}\n\n`;
- md += `**模型:** ${session.model} \n`;
- md += `**时间:** ${formatTime(session.createdAt)}\n\n---\n\n`;
-
- session.messages.forEach(m => {
- const role = m.role === 'user' ? '👤 用户' : '🤖 AI';
- md += `### ${role}\n\n${m.content || ''}\n\n`;
- if (m.files && m.files.length > 0) {
- md += m.files.map(f => `📎 \`${f.name}\``).join(' · ') + '\n\n';
- }
- if (m.think) md += `> **思考:** ${m.think}\n\n`;
- });
-
- downloadFile(`${session.title}.md`, md, 'text/markdown');
-}
-
-/** 导出为 HTML */
-export function exportAsHtml(session) {
- let html = `${escapeHtml(session.title)}
-
-${escapeHtml(session.title)} ${formatTime(session.createdAt)} · ${session.model}
`;
-
- session.messages.forEach(m => {
- const fileChips = (m.files && m.files.length > 0)
- ? ' ' + m.files.map(f => `📎 ${escapeHtml(f.name)} `).join(' ')
- : '';
- html += `${m.role === 'user' ? '👤 用户' : '🤖 AI'} ${fileChips} ${(m.content || '').replace(/\n/g, ' ')}
`;
- });
-
- html += '';
- downloadFile(`${session.title}.html`, html, 'text/html');
-}
-
-/** 导出为 TXT */
-export function exportAsTxt(session) {
- let txt = `${session.title}\n模型: ${session.model}\n时间: ${formatTime(session.createdAt)}\n${'='.repeat(50)}\n\n`;
-
- session.messages.forEach(m => {
- txt += `[${m.role === 'user' ? '用户' : 'AI'}]\n`;
- if (m.files && m.files.length > 0) {
- txt += m.files.map(f => `📎 ${f.name}`).join(' · ') + '\n';
- }
- txt += `${m.content || ''}\n\n`;
- });
-
- downloadFile(`${session.title}.txt`, txt, 'text/plain');
-}
diff --git a/js/components/header.js b/js/components/header.js
deleted file mode 100644
index d4da936..0000000
--- a/js/components/header.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * Header - 顶部导航组件
- * 包含连接状态检测、新建会话按钮
- */
-
-import { state, KEYS } from '../state.js';
-import { formatSize } from '../utils.js';
-import { OllamaAPI } from '../ollama-api.js';
-
-let connStatusEl;
-
-export function initHeader() {
- connStatusEl = document.querySelector('#connStatus');
-}
-
-/**
- * 检测连接状态并更新 UI
- * @returns {Promise<{ok: boolean, version?: object, error?: Error}>}
- */
-export async function checkConnection() {
- const api = state.get(KEYS.API);
- if (!api) return { ok: false, error: new Error('API 未初始化') };
-
- connStatusEl.className = 'conn-status pending';
- connStatusEl.querySelector('.status-label').textContent = '连接中...';
-
- try {
- const version = await api.getVersion();
- connStatusEl.className = 'conn-status connected';
- connStatusEl.title = `已连接 (v${version.version})`;
- connStatusEl.querySelector('.status-label').textContent = '已连接';
- return { ok: true, version };
- } catch (err) {
- connStatusEl.className = 'conn-status disconnected';
- connStatusEl.title = '未连接';
- connStatusEl.querySelector('.status-label').textContent = '未连接';
- return { ok: false, error: err };
- }
-}
-
-/**
- * 更新设置面板中的连接信息
- */
-export async function updateConnectionInfo() {
- const api = state.get(KEYS.API);
- if (!api) return;
-
- const badge = document.querySelector('#connBadge');
- const verEl = document.querySelector('#serverVersion');
- const corsHint = document.querySelector('#corsHint');
-
- badge.textContent = '检测中...';
- badge.className = 'status-badge warning';
- verEl.textContent = '';
-
- try {
- const version = await api.getVersion();
- badge.textContent = '✓ 已连接';
- badge.className = 'status-badge success';
- verEl.textContent = `v${version.version}`;
- corsHint.style.display = 'none';
- } catch (err) {
- badge.textContent = '✗ 未连接';
- badge.className = 'status-badge error';
- verEl.textContent = '';
-
- if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError') || err.message.includes('CORS')) {
- corsHint.style.display = '';
- } else {
- corsHint.style.display = 'none';
- }
- }
-}
-
-/**
- * 更新运行中的模型列表
- */
-export async function updateRunningModels() {
- const api = state.get(KEYS.API);
- if (!api) return;
-
- const container = document.querySelector('#runningModels');
- try {
- const data = await api.psModels();
- if (!data.models || data.models.length === 0) {
- container.innerHTML = '无运行中的模型
';
- return;
- }
- container.innerHTML = data.models.map(m => `
-
- ${m.name}
- ${formatSize(m.size_vram || 0)} VRAM
-
- `).join('');
- } catch {
- container.innerHTML = '无法获取运行信息
';
- }
-}
diff --git a/js/components/history-modal.js b/js/components/history-modal.js
deleted file mode 100644
index eb8d457..0000000
--- a/js/components/history-modal.js
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * HistoryModal - 历史记录面板组件
- */
-
-import { state, KEYS } from '../state.js';
-import { debounce, escapeHtml, formatTime } from '../utils.js';
-import { exportAsMarkdown, exportAsHtml, exportAsTxt, renderMessages, clearMessages } from './chat-area.js';
-
-const HISTORY_PAGE_SIZE = 10;
-
-let historyModalEl, historyListEl, historyPaginationEl;
-let historyPage = 1;
-let historySearchQuery = '';
-
-export function initHistoryModal() {
- historyModalEl = document.querySelector('#historyModal');
- historyListEl = document.querySelector('#historyList');
- historyPaginationEl = document.querySelector('#historyPagination');
-
- // 打开/关闭
- document.querySelector('#btnHistory').addEventListener('click', openHistoryModal);
- document.querySelector('#btnCloseHistory').addEventListener('click', closeHistoryModal);
- historyModalEl.addEventListener('click', (e) => {
- if (e.target === historyModalEl) closeHistoryModal();
- });
-
- // 退出历史只读模式
- document.querySelector('#btnExitHistory').addEventListener('click', () => {
- document.querySelector('#btnNewChat').click();
- });
-
- // 搜索
- const historySearchInput = document.querySelector('#historySearchInput');
- const historySearchClear = document.querySelector('#historySearchClear');
- const doHistorySearch = debounce(() => {
- historySearchQuery = historySearchInput.value.trim();
- historySearchClear.style.display = historySearchQuery ? '' : 'none';
- historyPage = 1;
- loadHistory();
- }, 250);
- historySearchInput.addEventListener('input', doHistorySearch);
- historySearchClear.addEventListener('click', () => {
- historySearchInput.value = '';
- historySearchQuery = '';
- historySearchClear.style.display = 'none';
- historyPage = 1;
- loadHistory();
- });
-
- // 历史列表事件委托
- historyListEl.addEventListener('click', async (e) => {
- const target = e.target.closest('[data-id]');
- if (!target) return;
- const sessionId = target.dataset.id;
- const db = state.get(KEYS.DB);
-
- if (target.classList.contains('btn-delete-session')) {
- if (confirm('确定删除此会话?')) {
- await db.deleteSession(sessionId);
- const currentSession = state.get(KEYS.CURRENT_SESSION);
- if (currentSession && currentSession.id === sessionId) {
- document.querySelector('#btnNewChat').click();
- }
- loadHistory();
- }
- } else if (target.classList.contains('btn-export-md')) {
- const session = await db.getSession(sessionId);
- if (session) exportAsMarkdown(session);
- } else if (target.classList.contains('btn-export-html')) {
- const session = await db.getSession(sessionId);
- if (session) exportAsHtml(session);
- } else if (target.classList.contains('btn-export-txt')) {
- const session = await db.getSession(sessionId);
- if (session) exportAsTxt(session);
- } else if (target.classList.contains('history-info') || target.classList.contains('history-item')) {
- loadHistorySession(sessionId);
- }
- });
-
- // 分页事件
- historyPaginationEl.addEventListener('click', (e) => {
- const btn = e.target.closest('.page-btn');
- if (!btn || btn.classList.contains('disabled')) return;
- const page = parseInt(btn.dataset.page);
- if (page && page !== historyPage) {
- historyPage = page;
- loadHistory();
- historyListEl.scrollTop = 0;
- }
- });
-}
-
-export async function loadHistory() {
- const db = state.get(KEYS.DB);
- if (!db) return;
-
- let allSessions = await db.getAllSessions();
-
- if (historySearchQuery) {
- const q = historySearchQuery.toLowerCase();
- allSessions = allSessions.filter(s => {
- if (s.title?.toLowerCase().includes(q)) return true;
- if (s.model?.toLowerCase().includes(q)) return true;
- return s.messages.some(m => m.content?.toLowerCase().includes(q));
- });
- }
-
- allSessions.sort((a, b) => b.updatedAt - a.updatedAt);
-
- if (allSessions.length === 0) {
- historyListEl.innerHTML = `
- `;
- historyPaginationEl.innerHTML = '';
- return;
- }
-
- const totalPages = Math.ceil(allSessions.length / HISTORY_PAGE_SIZE);
- if (historyPage > totalPages) historyPage = totalPages;
-
- const start = (historyPage - 1) * HISTORY_PAGE_SIZE;
- const pageSessions = allSessions.slice(start, start + HISTORY_PAGE_SIZE);
-
- historyListEl.innerHTML = pageSessions.map(s => `
-
-
-
${escapeHtml(s.title)}
-
- ${formatTime(s.updatedAt)}
- ${s.messages.length} 条消息
- ${s.model || '无模型'}
-
-
-
- 📄
- 🌐
- 📝
- 🗑️
-
-
- `).join('');
-
- // 分页
- if (totalPages <= 1) {
- historyPaginationEl.innerHTML = `共 ${allSessions.length} 条 `;
- return;
- }
-
- let html = `共 ${allSessions.length} 条 `;
- html += '';
- html += `‹ `;
-
- const range = 2;
- let pages = [];
- for (let i = 1; i <= totalPages; i++) {
- if (i === 1 || i === totalPages || (i >= historyPage - range && i <= historyPage + range)) {
- pages.push(i);
- }
- }
- let prev = 0;
- for (const p of pages) {
- if (p - prev > 1) html += '… ';
- html += `${p} `;
- prev = p;
- }
-
- html += `› `;
- html += '
';
- historyPaginationEl.innerHTML = html;
-}
-
-async function loadHistorySession(sessionId) {
- const db = state.get(KEYS.DB);
- if (!db) return;
-
- const session = await db.getSession(sessionId);
- if (!session) return;
-
- state.set(KEYS.CURRENT_SESSION, session);
- state.set(KEYS.IS_HISTORY_VIEW, true);
-
- document.querySelector('#historyBar').style.display = '';
- document.querySelector('#inputArea').style.display = 'none';
-
- clearMessages();
- renderMessages();
- closeHistoryModal();
-}
-
-export function openHistoryModal() {
- historyPage = 1;
- historySearchQuery = '';
- document.querySelector('#historySearchInput').value = '';
- document.querySelector('#historySearchClear').style.display = 'none';
- loadHistory();
- historyModalEl.style.display = '';
-}
-
-export function closeHistoryModal() {
- historyModalEl.style.display = 'none';
-}
diff --git a/js/components/input-area.js b/js/components/input-area.js
deleted file mode 100644
index 857b573..0000000
--- a/js/components/input-area.js
+++ /dev/null
@@ -1,650 +0,0 @@
-/**
- * InputArea - 输入区域组件
- * 包含文本输入、图片上传、文件上传、发送逻辑
- */
-
-import { state, KEYS } from '../state.js';
-import { fileToBase64, fileToText, detectLanguage, debounce, truncate, escapeHtml, formatSize } from '../utils.js';
-import { getSelectedModel, isThinkEnabled, isVisionAvailable } from './model-bar.js';
-import {
- renderMessages, appendAssistantPlaceholder, appendSystemMessage,
- updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll
-} from './chat-area.js';
-import { showToast } from './toast.js';
-import { checkConnection } from './header.js';
-import { isRagEnabled, performRagRetrieval } from './kb-modal.js';
-
-let chatInputEl, btnSendEl, fileInputEl, textFileInputEl, imagePreviewEl, filePreviewEl;
-let pendingImages = [];
-let pendingFiles = [];
-
-/** 常见文本/代码文件扩展名集合(用于校验) */
-const TEXT_EXTENSIONS = new Set([
- 'txt','md','markdown','rst','log','csv','tsv',
- 'py','pyw','pyi','js','mjs','cjs','ts','tsx','jsx',
- 'java','c','h','cpp','cc','cxx','hpp','go','rs','rb','php',
- 'sh','bash','zsh','fish','sql','html','htm','css',
- 'json','jsonl','xml','svg','yaml','yml','toml','ini','cfg','conf',
- 'swift','kt','kts','scala','lua','r','m','mm',
- 'cs','fs','vb','ex','exs','erl','hs','clj','lisp',
- 'diff','patch','pl','dockerfile','makefile'
-]);
-
-/** 最大文件大小 500KB */
-const MAX_FILE_SIZE = 500 * 1024;
-
-/** 根据文件名返回图标 */
-function getFileIcon(filename) {
- const name = filename.toLowerCase();
- if (name === 'dockerfile') return '🐳';
- if (name === 'makefile' || name === 'gnumakefile') return '🔨';
- const ext = filename.split('.').pop().toLowerCase();
- const map = {
- py: '🐍', pyw: '🐍', pyi: '🐍',
- js: '💛', mjs: '💛', cjs: '💛',
- ts: '🔷', tsx: '⚛️', jsx: '⚛️',
- java: '☕', go: '🐹', rs: '🦀', rb: '💎',
- php: '🐘', swift: '🍎', kt: '🤖', scala: '🔴',
- lua: '🌙', pl: '🐪', r: '📐', m: '📐',
- cs: '🟪', fs: '🔵', vb: '🔷',
- ex: '💧', exs: '💧', erl: '📞', hs: '🟣',
- clj: '🟢', lisp: '🟢',
- cpp: '⚙️', cc: '⚙️', cxx: '⚙️', hpp: '⚙️',
- c: '⚙️', h: '⚙️',
- sh: '🐚', bash: '🐚', zsh: '🐚', fish: '🐚',
- html: '🌐', htm: '🌐', css: '🎨', svg: '🎨',
- json: '📋', yaml: '📋', yml: '📋', toml: '📋',
- ini: '📋', cfg: '📋', conf: '📋',
- xml: '📰', sql: '🗃️',
- md: '📝', markdown: '📝', rst: '📝', txt: '📄',
- log: '📜', csv: '📊', tsv: '📊',
- diff: '🔀', patch: '🔀',
- };
- return map[ext] || '📄';
-}
-
-/** 判断是否为文本/代码文件 */
-function isTextFile(file) {
- // 无扩展名的特殊文件名
- const name = file.name.toLowerCase();
- if (name === 'dockerfile' || name === 'makefile') return true;
- const ext = name.split('.').pop();
- return TEXT_EXTENSIONS.has(ext);
-}
-
-export function initInputArea() {
- chatInputEl = document.querySelector('#chatInput');
- btnSendEl = document.querySelector('#btnSend');
- fileInputEl = document.querySelector('#fileInput');
- textFileInputEl = document.querySelector('#textFileInput');
- imagePreviewEl = document.querySelector('#imagePreview');
- filePreviewEl = document.querySelector('#filePreview');
-
- // 发送 / 停止
- btnSendEl.addEventListener('click', () => {
- if (state.get(KEYS.IS_STREAMING)) {
- stopGeneration();
- } else {
- sendMessage();
- }
- });
- chatInputEl.addEventListener('keydown', (e) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault();
- sendMessage();
- }
- });
-
- // 输入框自动调整
- chatInputEl.addEventListener('input', autoResizeTextarea);
-
- // 图片上传
- document.querySelector('#btnAttachImg').addEventListener('click', () => {
- if (!isVisionAvailable()) {
- showToast('当前模型不支持图片分析', 'warning');
- return;
- }
- fileInputEl.click();
- });
- fileInputEl.addEventListener('change', (e) => {
- handleImageSelect(e.target.files);
- e.target.value = '';
- });
-
- // 文件上传(文本/代码)
- document.querySelector('#btnAttachFile').addEventListener('click', () => textFileInputEl.click());
- textFileInputEl.addEventListener('change', (e) => {
- handleTextFileSelect(e.target.files);
- e.target.value = '';
- });
-
- // 删除图片预览
- imagePreviewEl.addEventListener('click', (e) => {
- if (e.target.classList.contains('remove-img')) {
- const idx = parseInt(e.target.dataset.index);
- pendingImages.splice(idx, 1);
- renderImagePreviews();
- }
- });
-
- // 删除文件预览
- filePreviewEl.addEventListener('click', (e) => {
- if (e.target.classList.contains('remove-file')) {
- const idx = parseInt(e.target.dataset.index);
- pendingFiles.splice(idx, 1);
- renderFilePreviews();
- }
- });
-}
-
-function autoResizeTextarea() {
- chatInputEl.style.height = 'auto';
- chatInputEl.style.height = Math.min(chatInputEl.scrollHeight, 120) + 'px';
-}
-
-// ── 图片处理 ──
-
-async function handleImageSelect(files) {
- const maxSize = 20 * 1024 * 1024;
- for (const file of files) {
- if (!file.type.startsWith('image/')) continue;
- if (file.size > maxSize) {
- appendSystemMessage(`⚠️ 图片 ${file.name} 超过 20MB 限制`);
- continue;
- }
- try {
- const base64 = await fileToBase64(file);
- pendingImages.push({ name: file.name, base64 });
- renderImagePreviews();
- } catch (err) {
- console.error('[InputArea] 图片读取失败:', err);
- }
- }
-}
-
-function renderImagePreviews() {
- if (pendingImages.length === 0) {
- imagePreviewEl.style.display = 'none';
- imagePreviewEl.innerHTML = '';
- return;
- }
- imagePreviewEl.style.display = '';
- imagePreviewEl.innerHTML = pendingImages.map((img, i) => `
-
-
-
×
-
- `).join('');
-}
-
-// ── 文件处理(文本/代码) ──
-
-async function handleTextFileSelect(files) {
- for (const file of files) {
- if (!isTextFile(file)) {
- appendSystemMessage(`⚠️ ${file.name} 不是支持的文本/代码文件`);
- continue;
- }
- if (file.size > MAX_FILE_SIZE) {
- appendSystemMessage(`⚠️ ${file.name} 超过 500KB 限制(${formatSize(file.size)})`);
- continue;
- }
- // 重复检测
- if (pendingFiles.some(f => f.name === file.name && f.size === file.size)) {
- appendSystemMessage(`ℹ️ ${file.name} 已添加`);
- continue;
- }
- try {
- const content = await fileToText(file);
- pendingFiles.push({
- name: file.name,
- content,
- language: detectLanguage(file.name),
- size: file.size
- });
- renderFilePreviews();
- } catch (err) {
- console.error('[InputArea] 文件读取失败:', err);
- appendSystemMessage(`❌ 读取 ${file.name} 失败`);
- }
- }
-}
-
-function renderFilePreviews() {
- if (pendingFiles.length === 0) {
- filePreviewEl.style.display = 'none';
- filePreviewEl.innerHTML = '';
- return;
- }
- filePreviewEl.style.display = '';
- filePreviewEl.innerHTML = pendingFiles.map((f, i) => `
-
- ${getFileIcon(f.name)}
- ${escapeHtml(f.name)}
- ${formatSize(f.size)}
- ×
-
- `).join('');
-}
-
-// ── UI 状态 ──
-
-function updateSendButton(streaming) {
- if (streaming) {
- btnSendEl.innerHTML = `
-
- `;
- btnSendEl.classList.add('stop-btn');
- btnSendEl.classList.remove('disabled');
- btnSendEl.title = '停止生成';
- } else {
- btnSendEl.innerHTML = `
-
- `;
- btnSendEl.classList.remove('stop-btn');
- btnSendEl.classList.remove('disabled');
- btnSendEl.title = '发送';
- }
-}
-
-/**
- * 停止生成
- */
-function stopGeneration() {
- const abortController = state.get(KEYS.ABORT_CONTROLLER);
- if (abortController) {
- abortController.abort();
- }
-}
-
-/**
- * 核心发送逻辑
- */
-
-/** 构建发送给 Ollama API 的消息数组:将文件内容拼入 content */
-function buildApiMessages(messages) {
- return messages.map(m => {
- let content = m.content || '';
- // 如果有文件内容,拼接到 API 消息的 content 中
- if (m._fileContents && m._fileContents.length > 0) {
- const fileParts = m._fileContents.map(f => {
- const lang = f.language || '';
- return `📄 以下是一个文件内容:\n\n\`\`\`${lang}\n${f.content}\n\`\`\``;
- });
- if (content) {
- content += '\n\n---\n' + fileParts.join('\n\n---\n');
- } else {
- const count = m._fileContents.length;
- content = `请分析以下 ${count > 1 ? count + ' 个' : ''}文件:\n\n${fileParts.join('\n\n---\n')}`;
- }
- }
- return {
- role: m.role,
- content,
- ...(m.images && { images: m.images })
- };
- });
-}
-
-export async function sendMessage() {
- const text = chatInputEl.value.trim();
- if (!text && pendingImages.length === 0 && pendingFiles.length === 0) return;
- if (state.get(KEYS.IS_STREAMING)) return;
-
- const model = getSelectedModel();
- if (!model) {
- appendSystemMessage('⚠️ 请先选择一个模型');
- return;
- }
-
- // 有图片但模型不支持 vision → 阻止发送
- if (pendingImages.length > 0 && !isVisionAvailable()) {
- appendSystemMessage('⚠️ 当前模型不支持图片分析,请移除图片后重试');
- return;
- }
-
- const currentSession = state.get(KEYS.CURRENT_SESSION);
- if (!currentSession) return;
-
- const now = Date.now();
- const msgsToAdd = [];
-
- // ── 构造用户消息:展示用(content=纯文本,files=元数据) ──
- const userFiles = pendingFiles.map(f => ({
- name: f.name,
- language: f.language,
- size: f.size
- }));
-
- // 场景1:有图片
- if (pendingImages.length > 0) {
- msgsToAdd.push({
- role: 'user',
- content: pendingImages.length === 1
- ? `[上传了图片: ${pendingImages[0].name}]`
- : `[上传了 ${pendingImages.length} 张图片]`,
- images: pendingImages.map(img => img.base64),
- timestamp: now
- });
- }
-
- // 场景2:有文本输入 + 文件,或者只有文件
- if (text || pendingFiles.length > 0) {
- const msg = {
- role: 'user',
- content: text || '',
- timestamp: now
- };
- if (userFiles.length > 0) {
- msg.files = userFiles;
- msg._fileContents = pendingFiles.map(f => ({
- language: f.language,
- content: f.content
- }));
- }
- msgsToAdd.push(msg);
- }
-
- // 更新会话标题 + 追加消息(通过 update 确保引用变化,触发浅比较)
- const isFirstMsg = currentSession.messages.length === 0;
- state.update(KEYS.CURRENT_SESSION, session => ({
- ...session,
- ...(isFirstMsg && {
- title: truncate(
- text || (pendingFiles.length > 0 ? `[文件: ${pendingFiles.map(f => f.name).join(', ')}]` : '[图片消息]'),
- 30
- ),
- model
- }),
- messages: [...session.messages, ...msgsToAdd],
- updatedAt: Date.now()
- }));
-
- // update() 产生新引用,后续必须从 state 读取最新值
- const freshSession = state.get(KEYS.CURRENT_SESSION);
-
- enableAutoScroll();
- renderMessages();
-
- // 立即保存
- await saveCurrentSession();
-
- // 清空输入
- chatInputEl.value = '';
- pendingImages = [];
- pendingFiles = [];
- imagePreviewEl.style.display = 'none';
- imagePreviewEl.innerHTML = '';
- filePreviewEl.style.display = 'none';
- filePreviewEl.innerHTML = '';
- autoResizeTextarea();
-
- // 助手占位
- appendAssistantPlaceholder();
-
- // 流式回复
- state.set(KEYS.IS_STREAMING, true);
- updateSendButton(true);
-
- const api = state.get(KEYS.API);
- const abortController = new AbortController();
- state.set(KEYS.ABORT_CONTROLLER, abortController);
- const thinkMode = isThinkEnabled();
- const systemPromptEnabled = state.get(KEYS.SYSTEM_PROMPT_ENABLED);
- const systemPrompt = state.get(KEYS.SYSTEM_PROMPT);
- const numCtx = state.get(KEYS.NUM_CTX, 24576);
-
- let assistantContent = '';
- let thinkContent = '';
- let finalStats = null;
- let modelName = '';
-
- try {
- const apiMessages = buildApiMessages(freshSession.messages);
-
- const temperature = state.get('temperature', 0.7);
-
- const chatParams = {
- model,
- messages: apiMessages,
- think: thinkMode,
- ...(systemPromptEnabled && systemPrompt && { system: systemPrompt }),
- ...(numCtx && { options: { num_ctx: numCtx, temperature } })
- };
-
- // RAG 检索增强
- let ragSources = null;
- if (isRagEnabled()) {
- appendSystemMessage('🧠 正在检索知识库...', 'rag-status');
- try {
- const ragResult = await performRagRetrieval(text);
- if (ragResult && ragResult.results && ragResult.results.length > 0) {
- // 计算 RAG 上下文可用预算
- const ctxLimit = numCtx || 24576;
- const outputReserve = 2048; // 预留给模型输出
- // 估算已有 token 数(粗略:1 token ≈ 2 字符)
- const msgChars = apiMessages.reduce((sum, m) => sum + (m.content?.length || 0), 0);
- const systemChars = (chatParams.system?.length || 0);
- const usedTokens = Math.ceil((msgChars + systemChars) / 2);
- const ragBudget = Math.max(0, ctxLimit - outputReserve - usedTokens);
- const ragBudgetChars = ragBudget * 2; // 转换为字符数
-
- // 构建 RAG prompt,按预算截取上下文
- let ragContext = ragResult.results.map((r, i) =>
- `[来源 ${i + 1}: ${r.filename}]\n${r.text}`
- ).join('\n\n---\n\n');
-
- const ragTemplate = `你是一个知识库问答助手。以下是检索到的相关文档片段,请基于这些内容回答用户的问题。如果检索到的内容无法回答问题,请如实说明。
-
-=== 检索到的相关内容 ===
-
-=== 内容结束 ===
-
-请基于以上内容回答用户的问题。引用时请注明来源编号(如"来源 1")。`;
- const templateOverhead = ragTemplate.length; // 模板本身占用的字符数
-
- if (ragContext.length + templateOverhead > ragBudgetChars) {
- ragContext = ragContext.slice(0, Math.max(0, ragBudgetChars - templateOverhead - 50)) + '\n\n[知识库内容过长,已截取最相关的部分]';
- appendSystemMessage(`⚠️ 知识库内容已截取(预算 ${ragBudgetChars} 字符)`, 'rag-status');
- }
-
- const ragPrompt = `你是一个知识库问答助手。以下是检索到的相关文档片段,请基于这些内容回答用户的问题。如果检索到的内容无法回答问题,请如实说明。
-
-=== 检索到的相关内容 ===
-${ragContext}
-=== 内容结束 ===
-
-请基于以上内容回答用户的问题。引用时请注明来源编号(如"来源 1")。`;
-
- chatParams.system = chatParams.system
- ? chatParams.system + '\n\n' + ragPrompt
- : ragPrompt;
-
- ragSources = ragResult.results.map(r => ({
- filename: r.filename,
- score: r.score,
- text: truncate(r.text, 100)
- }));
- const sourceNames = [...new Set(ragSources.map(s => s.filename))];
- appendSystemMessage(`🧠 已检索 ${ragSources.length} 个相关片段(来源:${sourceNames.join('、')})`, 'rag-status');
- } else {
- appendSystemMessage('🧠 知识库未检索到相关内容,使用通用知识回答', 'rag-status');
- }
- } catch (ragErr) {
- console.error('[RAG] 检索失败:', ragErr);
- appendSystemMessage(`🧠 知识库检索失败: ${ragErr.message}`, 'rag-status');
- }
- }
-
- console.log('[RAG] numCtx:', numCtx, 'system长度:', chatParams.system?.length || 0);
- console.log('[RAG] 开始调用 chatStream...');
-
- let ragStatusCleared = false;
-
- await api.chatStream(chatParams, (chunk) => {
- // 首个有效内容到达时才清除 RAG 状态,避免检索完到开始回复之间的空白
- if (!ragStatusCleared && chunk.message?.content) {
- ragStatusCleared = true;
- document.querySelectorAll('.rag-status').forEach(el => el.remove());
- }
- if (chunk.message) {
- if (chunk.message.content) {
- assistantContent += chunk.message.content;
- }
- const think = chunk.message.thinking || chunk.message.reasoning_content;
- if (think && typeof think === 'string') {
- thinkContent += think;
- }
- }
- if (chunk.model) modelName = chunk.model;
- updateLastAssistantMessage(assistantContent, thinkContent || null, null, modelName || null);
- if (chunk.done) {
- finalStats = { eval_count: chunk.eval_count, total_duration: chunk.total_duration };
- }
- }, abortController);
-
- // 如果流结束但没有任何内容
- if (!assistantContent && !thinkContent) {
- appendSystemMessage('⚠️ 模型未返回任何内容,可能是上下文过长或模型不支持当前请求');
- }
-
- const assistantMsg = {
- role: 'assistant',
- content: assistantContent || '',
- timestamp: Date.now(),
- ...(modelName && { model: modelName }),
- ...(thinkContent && { think: thinkContent }),
- ...(finalStats && { eval_count: finalStats.eval_count, total_duration: finalStats.total_duration }),
- ...(ragSources && { ragSources })
- };
- state.update(KEYS.CURRENT_SESSION, session => ({
- ...session,
- messages: [...session.messages, assistantMsg],
- updatedAt: Date.now()
- }));
-
- if (finalStats) {
- updateLastAssistantMessage(assistantContent, thinkContent || null, finalStats, modelName || null);
- }
-
- // 流结束后插入 RAG 来源到现有 DOM(renderMessages 不会重新渲染已有消息)
- if (ragSources && ragSources.length > 0) {
- const lastAssistant = document.querySelector('#messagesContainer .message.assistant:last-of-type');
- if (lastAssistant) {
- const sourcesHtml = ragSources.map((s, i) => {
- const scorePercent = s.score ? `${(s.score * 100).toFixed(0)}%` : '';
- return `📄 来源 ${i + 1}: ${escapeHtml(s.filename)} ${scorePercent ? `${scorePercent} ` : ''}
`;
- }).join('');
- const ragDiv = document.createElement('div');
- ragDiv.className = 'rag-sources';
- ragDiv.innerHTML = `${sourcesHtml}`;
- lastAssistant.querySelector('.msg-body').appendChild(ragDiv);
- }
- }
-
- await saveCurrentSession();
-
- } catch (err) {
- console.error('[InputArea] 流式聊天错误:', err);
-
- // 清除 RAG 临时状态消息
- document.querySelectorAll('.rag-status').forEach(el => el.remove());
-
- if (err.name === 'AbortError') {
- const placeholder = document.querySelector('#messagesContainer .message.assistant.loading');
- const loadingDots = placeholder?.querySelector('.loading-dots');
- const loadingText = placeholder?.querySelector('.loading-text');
- if (loadingDots) loadingDots.remove();
- if (loadingText) loadingText.remove();
- placeholder?.classList.remove('loading');
-
- const contentDiv = placeholder?.querySelector('.msg-content');
-
- const partialMsg = {
- role: 'assistant',
- content: assistantContent,
- timestamp: Date.now(),
- ...(modelName && { model: modelName }),
- ...(thinkContent && { think: thinkContent }),
- stopped: true
- };
- state.update(KEYS.CURRENT_SESSION, session => ({
- ...session,
- messages: [...session.messages, partialMsg],
- updatedAt: Date.now()
- }));
-
- if (contentDiv) {
- let html = '';
- if (assistantContent) {
- html = safeMarkdown(assistantContent);
- }
- html += '[已停止]
';
- contentDiv.innerHTML = html;
- }
-
- appendSystemMessage('⏹ 已停止生成');
- await saveCurrentSession();
-
- state.set(KEYS.IS_STREAMING, false);
- updateSendButton(false);
- state.set(KEYS.ABORT_CONTROLLER, null);
- return;
- }
-
- const placeholder = document.querySelector('#messagesContainer .message.assistant.loading');
- const loadingDots = placeholder?.querySelector('.loading-dots');
- const loadingText = placeholder?.querySelector('.loading-text');
- if (loadingDots) loadingDots.remove();
- if (loadingText) loadingText.remove();
- placeholder?.classList.remove('loading');
-
- const contentDiv = placeholder?.querySelector('.msg-content');
-
- if (assistantContent && contentDiv) {
- const partialMsg = {
- role: 'assistant',
- content: assistantContent + '\n\n`[已中断]`',
- timestamp: Date.now(),
- };
- state.update(KEYS.CURRENT_SESSION, session => ({
- ...session,
- messages: [...session.messages, partialMsg],
- updatedAt: Date.now()
- }));
- contentDiv.innerHTML = safeMarkdown(assistantContent) + '[已中断]
';
- await saveCurrentSession();
- } else {
- if (placeholder) placeholder.remove();
- }
-
- let errMsg = `❌ 错误: ${err.message}`;
- console.error('[InputArea] 完整错误:', err);
- if (err.message.includes('Failed to fetch') || err.message.includes('NetworkError')) {
- errMsg = '❌ 连接失败。请检查:\n1. Ollama 是否正在运行\n2. 地址是否正确\n3. 是否设置了 OLLAMA_ORIGINS="*"';
- } else if (err.message.includes('400')) {
- errMsg = '❌ 请求参数错误,可能是知识库内容超出模型上下文限制';
- } else if (err.message.includes('500')) {
- errMsg = '❌ Ollama 服务器错误,请检查 Ollama 日志';
- }
- appendSystemMessage(errMsg);
-
- } finally {
- state.set(KEYS.IS_STREAMING, false);
- updateSendButton(false);
- state.set(KEYS.ABORT_CONTROLLER, null);
- }
-}
-
-async function saveCurrentSession() {
- const db = state.get(KEYS.DB);
- const currentSession = state.get(KEYS.CURRENT_SESSION);
- const isHistoryView = state.get(KEYS.IS_HISTORY_VIEW);
- if (!currentSession || !db || isHistoryView) return;
- currentSession.updatedAt = Date.now();
- try {
- await db.saveSession(currentSession);
- } catch (err) {
- console.error('[InputArea] 保存会话失败:', err);
- }
-}
diff --git a/js/components/kb-modal.js b/js/components/kb-modal.js
deleted file mode 100644
index 571494a..0000000
--- a/js/components/kb-modal.js
+++ /dev/null
@@ -1,346 +0,0 @@
-/**
- * KBModal - 知识库管理面板
- *
- * 支持:创建/删除集合、上传/删除文档、选择嵌入模型、启用 RAG 检索
- */
-
-import { state, KEYS } from '../state.js';
-import {
- initVectorStore, getVectorStore, addDocumentToKB,
- removeDocumentFromKB, getDocumentsInCollection, retrieveContext,
- buildRagSystemPrompt
-} from '../rag.js';
-import { fileToText, formatSize, escapeHtml, truncate } from '../utils.js';
-import { showToast } from './toast.js';
-
-let kbModalEl;
-let currentColId = null;
-
-/** 当前是否启用 RAG */
-let ragEnabled = false;
-let ragCollectionId = null;
-
-export function initKBModal() {
- kbModalEl = document.querySelector('#kbModal');
-
- document.querySelector('#btnKB').addEventListener('click', openKBModal);
- document.querySelector('#btnCloseKB').addEventListener('click', closeKBModal);
- kbModalEl.addEventListener('click', (e) => {
- if (e.target === kbModalEl) closeKBModal();
- });
-
- // 新建集合
- document.querySelector('#btnNewCollection').addEventListener('click', createCollection);
-
- // 上传文档
- document.querySelector('#btnUploadDoc').addEventListener('click', () => {
- if (!currentColId) {
- showToast('请先选择一个知识库集合', 'warning');
- return;
- }
- document.querySelector('#kbFileInput').click();
- });
- document.querySelector('#kbFileInput').addEventListener('change', handleDocUpload);
-
- // RAG 开关
- document.querySelector('#toggleRAG').addEventListener('change', (e) => {
- ragEnabled = e.target.checked;
- if (ragEnabled && currentColId) {
- ragCollectionId = currentColId;
- showToast('RAG 知识检索已开启', 'success');
- } else if (ragEnabled && !currentColId) {
- e.target.checked = false;
- ragEnabled = false;
- showToast('请先选择一个知识库集合', 'warning');
- return;
- } else {
- ragCollectionId = null;
- showToast('RAG 知识检索已关闭', 'info');
- }
- updateRagBadge();
- });
-
- // 嵌入模型变更
- document.querySelector('#selectEmbedModel').addEventListener('change', async (e) => {
- if (!currentColId) return;
- const vs = getVectorStore();
- const col = await vs.getCollection(currentColId);
- if (col) {
- col.embeddingModel = e.target.value;
- await vs.updateCollection(col);
- }
- });
-}
-
-// ── RAG 接口 ──
-
-export function isRagEnabled() {
- return ragEnabled && !!ragCollectionId;
-}
-
-export function getRagCollectionId() {
- return ragCollectionId;
-}
-
-export async function performRagRetrieval(query) {
- if (!isRagEnabled()) return null;
- try {
- const { context, results } = await retrieveContext(query, ragCollectionId, 5);
- if (!context) return null;
- return { context, results, ragPrompt: buildRagSystemPrompt(context) };
- } catch (err) {
- console.warn('[KB] RAG 检索失败:', err);
- return null;
- }
-}
-
-// ── UI ──
-
-function updateRagBadge() {
- const badge = document.querySelector('#badgeRAG');
- if (!badge) return;
- badge.style.display = ragEnabled ? '' : 'none';
-}
-
-async function openKBModal() {
- kbModalEl.style.display = '';
- await refreshCollections();
- await populateEmbedModels();
-}
-
-function closeKBModal() {
- kbModalEl.style.display = 'none';
-}
-
-/**
- * 刷新集合列表
- */
-async function refreshCollections() {
- const vs = await initVectorStore();
- const collections = await vs.getCollections();
- const listEl = document.querySelector('#kbCollectionList');
-
- if (collections.length === 0) {
- listEl.innerHTML = '暂无知识库,点击上方「新建集合」创建
';
- currentColId = null;
- refreshDocList();
- return;
- }
-
- listEl.innerHTML = collections.map(c => `
-
-
- ${escapeHtml(c.name)}
- ${c.docCount || 0} 文档 · ${(c.chunkCount || 0)} 分块
-
-
-
-
-
-
-
- `).join('');
-
- // 点击选中集合
- listEl.querySelectorAll('.kb-collection-item').forEach(el => {
- el.addEventListener('click', async (e) => {
- if (e.target.closest('.kb-col-delete')) return;
- currentColId = el.dataset.id;
- await refreshCollections();
- await refreshDocList();
- // 设置嵌入模型下拉
- const vs = getVectorStore();
- const col = await vs.getCollection(currentColId);
- if (col?.embeddingModel) {
- document.querySelector('#selectEmbedModel').value = col.embeddingModel;
- }
- });
- });
-
- // 删除集合
- listEl.querySelectorAll('.kb-col-delete').forEach(btn => {
- btn.addEventListener('click', async (e) => {
- e.stopPropagation();
- const id = btn.dataset.id;
- if (!confirm('确定删除此知识库集合?所有文档将被清除。')) return;
- const vs = getVectorStore();
- await vs.deleteCollection(id);
- if (currentColId === id) currentColId = null;
- if (ragCollectionId === id) {
- ragEnabled = false;
- ragCollectionId = null;
- document.querySelector('#toggleRAG').checked = false;
- updateRagBadge();
- }
- showToast('集合已删除', 'success');
- await refreshCollections();
- await refreshDocList();
- });
- });
-}
-
-/**
- * 刷新文档列表
- */
-async function refreshDocList() {
- const docListEl = document.querySelector('#kbDocList');
- if (!currentColId) {
- docListEl.innerHTML = '← 选择一个知识库集合
';
- return;
- }
-
- const docs = await getDocumentsInCollection(currentColId);
- if (docs.length === 0) {
- docListEl.innerHTML = '此集合暂无文档,上传文件开始构建知识库
';
- return;
- }
-
- docListEl.innerHTML = docs.map(d => `
-
-
📄
-
- ${escapeHtml(d.filename)}
- ${d.chunkCount} 个分块
-
-
-
-
-
-
-
- `).join('');
-
- docListEl.querySelectorAll('.kb-doc-delete').forEach(btn => {
- btn.addEventListener('click', async () => {
- const docId = btn.dataset.docid;
- if (!confirm('确定删除此文档?')) return;
- await removeDocumentFromKB(currentColId, docId);
- showToast('文档已删除', 'success');
- await refreshCollections();
- await refreshDocList();
- });
- });
-}
-
-/**
- * 创建新集合
- */
-async function createCollection() {
- const nameInput = document.querySelector('#inputKBName');
- const name = nameInput.value.trim();
- if (!name) {
- showToast('请输入知识库名称', 'warning');
- return;
- }
- const vs = await initVectorStore();
- const col = await vs.createCollection(name);
- currentColId = col.id;
- nameInput.value = '';
- showToast(`知识库「${name}」已创建`, 'success');
- await refreshCollections();
- await refreshDocList();
-}
-
-/**
- * 上传文档处理
- */
-async function handleDocUpload(e) {
- const fileArr = Array.from(e.target.files || []);
- e.target.value = '';
-
- if (fileArr.length === 0) return;
-
- const embedModel = document.querySelector('#selectEmbedModel').value;
- if (!embedModel) {
- showToast('请先选择一个嵌入模型', 'warning');
- return;
- }
-
- const progressEl = document.querySelector('#kbProgress');
- const progressTextEl = document.querySelector('#kbProgressText');
-
- let successCount = 0;
- let failCount = 0;
-
- for (const file of fileArr) {
- if (file.size > 5 * 1024 * 1024) {
- showToast(`${file.name} 超过 5MB 限制`, 'warning');
- failCount++;
- continue;
- }
-
- try {
- progressEl.style.display = '';
- progressTextEl.textContent = `正在处理 ${file.name}...`;
-
- const content = await fileToText(file);
- const result = await addDocumentToKB(
- currentColId, file.name, content, embedModel,
- (done, total, msg) => {
- progressTextEl.textContent = `${file.name}: ${msg} (${done}/${total})`;
- }
- );
-
- showToast(`${file.name} 已添加到知识库(${result.chunkCount} 个分块)`, 'success');
- successCount++;
- } catch (err) {
- console.error('[KB] 文档处理失败:', err);
- showToast(`${file.name} 处理失败: ${err.message}`, 'error');
- failCount++;
- }
- }
-
- progressEl.style.display = 'none';
-
- if (fileArr.length > 1) {
- showToast(`上传完成:成功 ${successCount} 个,失败 ${failCount} 个`, failCount > 0 ? 'warning' : 'success');
- }
-
- await refreshCollections();
- await refreshDocList();
-}
-
-/**
- * 填充嵌入模型下拉(通过 /api/show 检测 embedding 能力)
- */
-async function populateEmbedModels() {
- const select = document.querySelector('#selectEmbedModel');
- const api = state.get(KEYS.API);
- if (!api) return;
-
- try {
- const data = await api.listModels();
- select.innerHTML = '选择嵌入模型... ';
- if (!data.models || data.models.length === 0) {
- select.innerHTML = '未安装任何模型 ';
- return;
- }
-
- // 并发检测每个模型的 capabilities
- const checks = await Promise.allSettled(
- data.models.map(async (m) => {
- const info = await api.showModel(m.name);
- const caps = info.capabilities || [];
- return { name: m.name, size: m.size, isEmbed: caps.includes('embedding') };
- })
- );
-
- const embedModels = checks
- .filter(r => r.status === 'fulfilled' && r.value.isEmbed)
- .map(r => r.value);
-
- embedModels.forEach(m => {
- const opt = document.createElement('option');
- opt.value = m.name;
- const size = m.size ? ` (${formatSize(m.size)})` : '';
- opt.textContent = m.name + size;
- select.appendChild(opt);
- });
-
- if (embedModels.length === 0) {
- select.innerHTML = '未检测到嵌入模型,请先 ollama pull ';
- }
- } catch (err) {
- console.warn('[KB] 加载模型列表失败:', err);
- }
-}
diff --git a/js/components/lightbox.js b/js/components/lightbox.js
deleted file mode 100644
index 794b64e..0000000
--- a/js/components/lightbox.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Lightbox - 图片预览组件
- */
-
-let lightboxEl, lightboxImg;
-
-export function initLightbox() {
- lightboxEl = document.querySelector('#lightbox');
- lightboxImg = document.querySelector('#lightboxImg');
-
- // 关闭事件
- lightboxEl.addEventListener('click', (e) => {
- if (e.target === lightboxEl) closeLightbox();
- });
- document.querySelector('#lightboxClose').addEventListener('click', closeLightbox);
-
- // 消息区域点击图片打开预览
- document.querySelector('#messagesContainer')?.addEventListener('click', (e) => {
- if (e.target.dataset.lightbox === 'true') {
- openLightbox(e.target.src);
- }
- });
-}
-
-export function openLightbox(src) {
- lightboxImg.src = src;
- lightboxEl.style.display = '';
-}
-
-export function closeLightbox() {
- lightboxEl.style.display = 'none';
- lightboxImg.src = '';
-}
diff --git a/js/components/model-bar.js b/js/components/model-bar.js
deleted file mode 100644
index b03b952..0000000
--- a/js/components/model-bar.js
+++ /dev/null
@@ -1,230 +0,0 @@
-/**
- * ModelBar - 模型选择栏组件
- * 包含模型下拉选择、Think 能力检测、模型信息展示
- */
-
-import { state, KEYS } from '../state.js';
-import { formatSize } from '../utils.js';
-
-let modelSelectEl, badgeThinkEl, badgeVisionEl, thinkCheckbox, thinkWrap, btnAttachImg;
-
-/** 缓存模型能力 { modelName: { think: bool, vision: bool } } */
-const modelCapabilityCache = new Map();
-
-export function initModelBar() {
- modelSelectEl = document.querySelector('#modelSelect');
- badgeThinkEl = document.querySelector('#badgeThink');
- badgeVisionEl = document.querySelector('#badgeVision');
- thinkCheckbox = document.querySelector('#toggleThink');
- thinkWrap = document.querySelector('#thinkToggleWrap');
- btnAttachImg = document.querySelector('#btnAttachImg');
-
- // 模型切换 → 检测能力 + 保存设置
- modelSelectEl.addEventListener('change', async () => {
- const model = modelSelectEl.value;
- if (db()) await db().saveSetting('selectedModel', model);
- state.set('_defaultModel', model);
- state.update(KEYS.CURRENT_SESSION, session => session ? ({ ...session, model }) : session);
- if (model) {
- await checkModelCapability(model);
- } else {
- setThinkAvailable(false);
- setVisionAvailable(false);
- badgeThinkEl.style.display = 'none';
- badgeVisionEl.style.display = 'none';
- }
- });
-}
-
-function db() { return state.get(KEYS.DB); }
-function api() { return state.get(KEYS.API); }
-
-/**
- * 加载模型列表
- */
-export async function loadModels() {
- if (!api()) return;
-
- try {
- const data = await api().listModels();
- modelSelectEl.innerHTML = '';
-
- if (!data.models || data.models.length === 0) {
- modelSelectEl.innerHTML = '未安装任何模型 ';
- badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
- setThinkAvailable(false); setVisionAvailable(false);
- return;
- }
-
- data.models
- .sort((a, b) => {
- const nameA = a.name.toLowerCase(), nameB = b.name.toLowerCase();
- if (nameA !== nameB) return nameA.localeCompare(nameB);
- return (a.size || 0) - (b.size || 0);
- })
- .forEach(m => {
- const opt = document.createElement('option');
- opt.value = m.name;
- const diskSize = formatSize(m.size);
- const label = diskSize ? `${m.name} · ${diskSize}` : m.name;
- opt.textContent = label;
- modelSelectEl.appendChild(opt);
- });
-
- // 恢复默认选中模型
- const defaultModel = state.get('_defaultModel', '');
- if (defaultModel) {
- modelSelectEl.value = defaultModel;
- }
-
- // 检测当前选中模型的能力
- if (modelSelectEl.value) {
- await checkModelCapability(modelSelectEl.value);
- }
-
- // 后台过滤:移除不支持 completion 的嵌入模型
- filterEmbedModels(data.models);
-
- } catch (err) {
- console.error('[ModelBar] 加载模型失败:', err);
- modelSelectEl.innerHTML = '加载失败 - 检查连接 ';
- badgeThinkEl.style.display = 'none'; badgeVisionEl.style.display = 'none';
- setThinkAvailable(false); setVisionAvailable(false);
- }
-}
-
-/**
- * 后台检测并过滤嵌入模型(不支持 completion 的模型)
- */
-async function filterEmbedModels(models) {
- for (const m of models) {
- try {
- const info = await api().showModel(m.name);
- const caps = info.capabilities || [];
- const isCompletion = caps.includes('completion');
- // 缓存能力
- modelCapabilityCache.set(m.name, {
- think: caps.includes('thinking'),
- vision: caps.includes('vision'),
- completion: isCompletion,
- });
- // 从下拉框移除不支持 completion 的模型
- if (!isCompletion) {
- const opt = modelSelectEl.querySelector(`option[value="${CSS.escape(m.name)}"]`);
- if (opt) opt.remove();
- }
- } catch { /* 忽略检测失败的模型 */ }
- }
- // 如果当前选中的模型被移除了,选中第一个可用模型
- if (!modelSelectEl.value && modelSelectEl.options.length > 0) {
- modelSelectEl.value = modelSelectEl.options[0].value;
- state.set('_defaultModel', modelSelectEl.value);
- if (db()) await db().saveSetting('selectedModel', modelSelectEl.value);
- await checkModelCapability(modelSelectEl.value);
- }
-}
-
-/**
- * 调用 /api/show 检测模型能力
- * @param {string} modelName
- */
-async function checkModelCapability(modelName) {
- // 命中缓存
- if (modelCapabilityCache.has(modelName)) {
- applyCapability(modelName, modelCapabilityCache.get(modelName));
- return;
- }
-
- if (!api()) return;
-
- try {
- const info = await api().showModel(modelName);
- const capabilities = info.capabilities || [];
- const caps = {
- think: capabilities.includes('thinking'),
- vision: capabilities.includes('vision'),
- completion: capabilities.includes('completion'),
- };
- modelCapabilityCache.set(modelName, caps);
- applyCapability(modelName, caps);
- } catch (err) {
- console.warn('[ModelBar] 获取模型能力失败:', err);
- // 降级:不支持 think
- const fallback = { think: false, vision: false, completion: true };
- modelCapabilityCache.set(modelName, fallback);
- applyCapability(modelName, fallback);
- }
-}
-
-/**
- * 根据能力设置 UI 状态
- */
-function applyCapability(modelName, caps) {
- setThinkAvailable(caps.think);
- setVisionAvailable(caps.vision);
-
- // 更新能力标签
- badgeThinkEl.style.display = caps.think ? '' : 'none';
- badgeVisionEl.style.display = caps.vision ? '' : 'none';
-}
-
-/**
- * 设置 Think 开关是否可用
- */
-function setThinkAvailable(available) {
- if (available) {
- thinkCheckbox.disabled = false;
- thinkWrap.title = 'Think 模式:启用深度推理';
- thinkWrap.classList.remove('unavailable');
- } else {
- thinkCheckbox.checked = false;
- thinkCheckbox.disabled = true;
- thinkWrap.title = '当前模型不支持 Think 模式';
- thinkWrap.classList.add('unavailable');
- }
-}
-
-/**
- * 设置图片上传按钮是否可用
- */
-function setVisionAvailable(available) {
- if (!btnAttachImg) return;
- if (available) {
- btnAttachImg.disabled = false;
- btnAttachImg.title = '上传图片';
- btnAttachImg.classList.remove('disabled');
- } else {
- btnAttachImg.disabled = true;
- btnAttachImg.title = '当前模型不支持图片分析';
- btnAttachImg.classList.add('disabled');
- }
-}
-
-/**
- * 当前选择的模型是否支持视觉分析
- */
-export function isVisionAvailable() {
- return btnAttachImg && !btnAttachImg.disabled;
-}
-
-/**
- * 获取当前选择的模型
- */
-export function getSelectedModel() {
- return modelSelectEl.value;
-}
-
-/**
- * 设置选中的模型
- */
-export function setSelectedModel(modelName) {
- modelSelectEl.value = modelName;
- modelSelectEl.dispatchEvent(new Event('change'));
-}
-
-/**
- * Think 模式是否开启
- */
-export function isThinkEnabled() {
- return thinkCheckbox.checked && !thinkCheckbox.disabled;
-}
diff --git a/js/components/preset-bar.js b/js/components/preset-bar.js
deleted file mode 100644
index 132c415..0000000
--- a/js/components/preset-bar.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * PresetBar - 预设栏组件
- *
- * 在模型栏下方显示预设胶囊按钮列表,点击即切换
- * 右侧"+"按钮打开管理面板
- */
-
-import { state, KEYS } from '../state.js';
-import {
- getPresets, getActivePresetId, activatePreset,
- createPreset, updatePreset, deletePreset
-} from '../preset-manager.js';
-import { showToast } from './toast.js';
-
-let presetBarEl;
-let presetListEl;
-let editingPresetId = null;
-
-export function initPresetBar() {
- // 创建预设栏 DOM
- const modelBar = document.querySelector('.model-bar');
- if (!modelBar) return;
-
- const bar = document.createElement('div');
- bar.className = 'preset-bar';
- bar.innerHTML = `
-
-
-
-
-
-
- `;
- modelBar.after(bar);
-
- presetBarEl = bar;
- presetListEl = bar.querySelector('#presetList');
-
- // 点击"+"打开管理面板
- bar.querySelector('#btnAddPreset').addEventListener('click', openPresetModal);
-
- // 监听预设变化,刷新 UI
- state.on('activePresetId', renderPresetBar);
- state.on('presets', renderPresetBar);
-
- // 初始渲染
- renderPresetBar();
-}
-
-/**
- * 渲染预设胶囊列表
- */
-function renderPresetBar() {
- if (!presetListEl) return;
-
- const presets = getPresets();
- const activeId = getActivePresetId();
-
- presetListEl.innerHTML = '';
-
- for (const preset of presets) {
- const chip = document.createElement('button');
- chip.className = 'preset-chip' + (preset.id === activeId ? ' active' : '');
- chip.title = preset.systemPrompt ? preset.systemPrompt.slice(0, 100) : '无系统提示词';
- chip.innerHTML = `${preset.icon} ${escapeHtml(preset.name)} `;
-
- chip.addEventListener('click', () => handlePresetClick(preset.id));
- // 右键编辑(仅自定义)
- chip.addEventListener('contextmenu', (e) => {
- if (!preset.builtIn) {
- e.preventDefault();
- editingPresetId = preset.id;
- openPresetModal(preset);
- }
- });
-
- presetListEl.appendChild(chip);
- }
-}
-
-async function handlePresetClick(presetId) {
- const currentId = getActivePresetId();
- if (presetId === currentId) return; // 已激活,不重复切换
-
- await activatePreset(presetId);
-
- const preset = getPresets().find(p => p.id === presetId);
- if (preset) {
- showToast(`已切换到 ${preset.icon} ${preset.name}`, 'success', 2000);
- }
-}
-
-// ═══════════════════════════════════════
-// 预设管理模态框
-// ═══════════════════════════════════════
-
-function openPresetModal(editPreset) {
- const modal = document.querySelector('#presetModal');
- const titleEl = modal.querySelector('#presetModalTitle');
- const nameInput = modal.querySelector('#inputPresetName');
- const iconInput = modal.querySelector('#inputPresetIcon');
- const spInput = modal.querySelector('#inputPresetSystemPrompt');
- const tempInput = modal.querySelector('#inputPresetTemp');
- const tempVal = modal.querySelector('#presetTempValue');
- const ctxInput = modal.querySelector('#inputPresetNumCtx');
- const thinkCheck = modal.querySelector('#togglePresetThink');
- const btnSave = modal.querySelector('#btnSavePreset');
- const btnDelete = modal.querySelector('#btnDeletePreset');
-
- if (editPreset && editPreset.id) {
- // 编辑模式
- editingPresetId = editPreset.id;
- titleEl.textContent = '✏️ 编辑预设';
- nameInput.value = editPreset.name;
- iconInput.value = editPreset.icon;
- spInput.value = editPreset.systemPrompt;
- tempInput.value = editPreset.temperature ?? 0.7;
- tempVal.textContent = (editPreset.temperature ?? 0.7).toFixed(1);
- ctxInput.value = editPreset.numCtx || 24576;
- thinkCheck.checked = editPreset.think ?? false;
- btnDelete.style.display = editPreset.builtIn ? 'none' : '';
- btnSave.textContent = '保存';
- } else {
- // 新建模式
- editingPresetId = null;
- titleEl.textContent = '✨ 新建预设';
- nameInput.value = '';
- iconInput.value = '🤖';
- spInput.value = '';
- tempInput.value = 0.7;
- tempVal.textContent = '0.7';
- ctxInput.value = 24576;
- thinkCheck.checked = false;
- btnDelete.style.display = 'none';
- btnSave.textContent = '创建';
- }
-
- modal.style.display = '';
- nameInput.focus();
-
- // 温度滑块实时更新
- tempInput.oninput = () => {
- tempVal.textContent = parseFloat(tempInput.value).toFixed(1);
- };
-}
-
-export function initPresetModal() {
- const modal = document.querySelector('#presetModal');
- if (!modal) return;
-
- // 关闭
- modal.querySelector('#btnClosePresetModal').addEventListener('click', () => {
- modal.style.display = 'none';
- });
- modal.addEventListener('click', (e) => {
- if (e.target === modal) modal.style.display = 'none';
- });
-
- // 保存
- modal.querySelector('#btnSavePreset').addEventListener('click', async () => {
- const name = modal.querySelector('#inputPresetName').value.trim();
- if (!name) {
- showToast('请输入预设名称', 'warning');
- return;
- }
-
- const data = {
- name,
- icon: modal.querySelector('#inputPresetIcon').value.trim() || '🤖',
- systemPrompt: modal.querySelector('#inputPresetSystemPrompt').value.trim(),
- temperature: parseFloat(modal.querySelector('#inputPresetTemp').value) || 0.7,
- numCtx: parseInt(modal.querySelector('#inputPresetNumCtx').value) || 24576,
- think: modal.querySelector('#togglePresetThink').checked
- };
-
- if (editingPresetId) {
- await updatePreset(editingPresetId, data);
- showToast('预设已更新', 'success', 2000);
- // 如果更新的是当前激活预设,重新应用
- if (editingPresetId === getActivePresetId()) {
- await activatePreset(editingPresetId);
- }
- } else {
- const created = await createPreset(data);
- // 自动激活新建的预设
- await activatePreset(created.id);
- showToast(`已创建并激活 ${data.icon} ${data.name}`, 'success', 2000);
- }
-
- modal.style.display = 'none';
- editingPresetId = null;
- });
-
- // 删除
- modal.querySelector('#btnDeletePreset').addEventListener('click', async () => {
- if (!editingPresetId) return;
- const preset = getPresets().find(p => p.id === editingPresetId);
- if (!preset) return;
-
- if (confirm(`确定删除预设 "${preset.icon} ${preset.name}" ?`)) {
- await deletePreset(editingPresetId);
- showToast('预设已删除', 'info', 2000);
- modal.style.display = 'none';
- editingPresetId = null;
- }
- });
-}
-
-function escapeHtml(str) {
- const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
- return String(str).replace(/[&<>"']/g, c => map[c]);
-}
diff --git a/js/components/settings-modal.js b/js/components/settings-modal.js
deleted file mode 100644
index d1a9a37..0000000
--- a/js/components/settings-modal.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/**
- * SettingsModal - 设置面板组件
- */
-
-import { state, KEYS } from '../state.js';
-import { debounce, formatSize } from '../utils.js';
-import { encryptData, decryptData, isMetonaFile } from '../crypto.js';
-import { updateConnectionInfo, updateRunningModels } from './header.js';
-import { loadModels } from './model-bar.js';
-import { showToast } from './toast.js';
-import { OllamaAPI } from '../ollama-api.js';
-
-let settingsModalEl;
-
-export function initSettingsModal() {
- settingsModalEl = document.querySelector('#settingsModal');
-
- // 打开/关闭
- document.querySelector('#btnSettings').addEventListener('click', openSettingsModal);
- document.querySelector('#btnCloseSettings').addEventListener('click', closeSettingsModal);
- settingsModalEl.addEventListener('click', (e) => {
- if (e.target === settingsModalEl) closeSettingsModal();
- });
-
- // 保存服务器地址
- const saveServerUrl = debounce(async () => {
- const url = document.querySelector('#inputServerUrl').value.trim();
- if (!url) return;
- const db = state.get(KEYS.DB);
- const api = new OllamaAPI(url);
- state.set(KEYS.API, api);
- if (db) await db.saveSetting('serverUrl', url);
- updateConnectionInfo();
- loadModels();
- }, 500);
- document.querySelector('#inputServerUrl').addEventListener('input', saveServerUrl);
-
- // 系统提示词开关
- document.querySelector('#toggleSystemPrompt').addEventListener('change', async (e) => {
- const db = state.get(KEYS.DB);
- state.set(KEYS.SYSTEM_PROMPT_ENABLED, e.target.checked);
- document.querySelector('#inputSystemPrompt').disabled = !e.target.checked;
- if (db) await db.saveSetting('systemPromptEnabled', e.target.checked);
- });
-
- // 保存系统提示词
- const saveSystemPrompt = debounce(async () => {
- const db = state.get(KEYS.DB);
- const val = document.querySelector('#inputSystemPrompt').value.trim();
- state.set(KEYS.SYSTEM_PROMPT, val);
- if (db) await db.saveSetting('systemPrompt', val);
- }, 500);
- document.querySelector('#inputSystemPrompt').addEventListener('input', saveSystemPrompt);
-
- // 保存上下文长度
- const saveNumCtx = debounce(async () => {
- const db = state.get(KEYS.DB);
- const val = document.querySelector('#inputNumCtx').value.trim();
- const numCtx = val ? parseInt(val) : 24576;
- state.set(KEYS.NUM_CTX, numCtx);
- if (db) await db.saveSetting('numCtx', numCtx);
- }, 500);
- document.querySelector('#inputNumCtx').addEventListener('input', saveNumCtx);
-
- // 温度滑块
- const tempSlider = document.querySelector('#inputTemperature');
- const tempDisplay = document.querySelector('#tempValue');
- tempSlider.addEventListener('input', () => {
- const val = parseFloat(tempSlider.value);
- tempDisplay.textContent = val.toFixed(1);
- });
- const saveTemperature = debounce(async () => {
- const db = state.get(KEYS.DB);
- const val = parseFloat(tempSlider.value);
- state.set('temperature', val);
- if (db) await db.saveSetting('temperature', val);
- }, 300);
- tempSlider.addEventListener('change', saveTemperature);
-
- // 释放显存
- document.querySelector('#btnReleaseVRAM').addEventListener('click', async () => {
- const api = state.get(KEYS.API);
- const model = document.querySelector('#modelSelect').value;
- try {
- await api.chat({ model: model || 'any', messages: [], keep_alive: 0 });
- showToast('显存已释放', 'success');
- updateRunningModels();
- } catch (err) {
- showToast(`释放失败: ${err.message}`, 'error');
- }
- });
-
- // 清空所有历史
- document.querySelector('#btnClearAllHistory').addEventListener('click', async () => {
- const db = state.get(KEYS.DB);
- if (confirm('确定清空所有历史记录?此操作不可恢复!')) {
- await db.clearAll();
- // 触发新会话
- document.querySelector('#btnNewChat').click();
- showToast('已清空所有历史记录', 'success');
- }
- });
-
- // 导出全部会话
- document.querySelector('#btnExportAll').addEventListener('click', exportAllSessions);
-
- // 导入会话
- const importFileInput = document.querySelector('#importFileInput');
- document.querySelector('#btnImportSessions').addEventListener('click', () => importFileInput.click());
- importFileInput.addEventListener('change', (e) => {
- if (e.target.files.length > 0) {
- importSessions(e.target.files[0]);
- e.target.value = '';
- }
- });
-}
-
-export function openSettingsModal() {
- settingsModalEl.style.display = '';
- document.querySelector('#inputServerUrl').value = state.get(KEYS.API)?.baseUrl || '';
- updateConnectionInfo();
- updateRunningModels();
-}
-
-export function closeSettingsModal() {
- settingsModalEl.style.display = 'none';
-}
-
-async function exportAllSessions() {
- const db = state.get(KEYS.DB);
- if (!db) return;
- const sessions = await db.getAllSessions();
- if (sessions.length === 0) {
- showToast('没有可导出的会话', 'warning');
- return;
- }
-
- const backup = {
- app: 'Metona Ollama Client',
- version: 1,
- exportedAt: new Date().toISOString(),
- count: sessions.length,
- sessions
- };
-
- try {
- const blob = await encryptData(backup);
- const ts = new Date().toISOString().slice(0, 10);
- const a = document.createElement('a');
- a.href = URL.createObjectURL(blob);
- a.download = `metona-backup-${ts}.metona`;
- a.click();
- showToast(`已导出 ${sessions.length} 个会话(已加密)`, 'success');
- } catch (err) {
- console.error('[Settings] 导出失败:', err);
- showToast(`导出失败: ${err.message}`, 'error');
- }
-}
-
-async function importSessions(file) {
- const db = state.get(KEYS.DB);
- if (!db) return;
-
- if (!isMetonaFile(file)) {
- showToast('仅支持导入 .metona 格式文件', 'error');
- return;
- }
-
- try {
- const buffer = await file.arrayBuffer();
- const data = await decryptData(buffer);
-
- let sessions;
- if (Array.isArray(data)) {
- sessions = data;
- } else if (data.sessions && Array.isArray(data.sessions)) {
- sessions = data.sessions;
- } else {
- showToast('文件内容格式错误:未找到会话数据', 'error');
- return;
- }
-
- const result = await db.importSessions(sessions);
- showToast(`导入完成:${result.imported} 个会话${result.skipped > 0 ? `,跳过 ${result.skipped} 个` : ''}`, 'success', 4000);
- } catch (err) {
- console.error('[Settings] 导入失败:', err);
- showToast(`导入失败: ${err.message}`, 'error');
- }
-}
diff --git a/js/components/toast.js b/js/components/toast.js
deleted file mode 100644
index 8f60d9a..0000000
--- a/js/components/toast.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Toast - 通知组件
- */
-
-let toastContainer = null;
-
-export function initToast() {
- toastContainer = document.querySelector('#toastContainer');
-}
-
-/**
- * 显示 Toast 通知
- * @param {string} text - 通知文本
- * @param {'info'|'success'|'warning'|'error'} type - 通知类型
- * @param {number} duration - 显示时长 (ms)
- */
-export function showToast(text, type = 'info', duration = 3000) {
- if (!toastContainer) return;
-
- const iconMap = { success: '✓', error: '✗', warning: '⚠', info: 'ℹ' };
- const toast = document.createElement('div');
- toast.className = `toast ${type}`;
- toast.innerHTML = `${iconMap[type] || 'ℹ'} ${text} `;
- toastContainer.appendChild(toast);
-
- setTimeout(() => {
- toast.classList.add('removing');
- toast.addEventListener('animationend', () => toast.remove());
- }, duration);
-}
diff --git a/js/crypto.js b/js/crypto.js
deleted file mode 100644
index f780afb..0000000
--- a/js/crypto.js
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * Crypto - Metona 会话加密模块
- *
- * 文件格式: [8B magic "METONA1\0"][16B salt][12B IV][AES-256-GCM ciphertext]
- * 后缀: .metona
- * 安全上下文(HTTPS)下使用 AES-256-GCM,否则降级为 XOR 混淆
- */
-
-const MAGIC = new TextEncoder().encode('METONA1\0');
-const PASSPHRASE = 'metona-ollama-v2';
-const PBKDF2_ITERATIONS = 100000;
-
-const SECURE = !!(globalThis.crypto && globalThis.crypto.subtle);
-
-/**
- * SHA-256 哈希 (降级模式用)
- */
-async function sha256(data) {
- if (SECURE) {
- const buf = await crypto.subtle.digest('SHA-256', data);
- return new Uint8Array(buf);
- }
- // 纯 JS SHA-256
- return sha256js(data);
-}
-
-/**
- * 纯 JS SHA-256 实现 (兼容非安全上下文)
- */
-function sha256js(message) {
- const K = new Uint32Array([
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
- ]);
-
- const bytes = message instanceof Uint8Array ? message : new Uint8Array(message);
- const bitLen = bytes.length * 8;
-
- // 预处理: padding
- const paddedLen = Math.ceil((bytes.length + 9) / 64) * 64;
- const padded = new Uint8Array(paddedLen);
- padded.set(bytes);
- padded[bytes.length] = 0x80;
- const view = new DataView(padded.buffer);
- view.setUint32(paddedLen - 4, bitLen, false);
-
- let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
- let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
-
- const w = new Uint32Array(64);
-
- for (let offset = 0; offset < paddedLen; offset += 64) {
- for (let i = 0; i < 16; i++) {
- w[i] = view.getUint32(offset + i * 4, false);
- }
- for (let i = 16; i < 64; i++) {
- const s0 = (w[i - 15] >>> 7 | w[i - 15] << 25) ^ (w[i - 15] >>> 18 | w[i - 15] << 14) ^ (w[i - 15] >>> 3);
- const s1 = (w[i - 2] >>> 17 | w[i - 2] << 15) ^ (w[i - 2] >>> 19 | w[i - 2] << 13) ^ (w[i - 2] >>> 10);
- w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
- }
-
- let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
-
- for (let i = 0; i < 64; i++) {
- const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
- const ch = (e & f) ^ (~e & g);
- const temp1 = (h + S1 + ch + K[i] + w[i]) >>> 0;
- const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
- const maj = (a & b) ^ (a & c) ^ (b & c);
- const temp2 = (S0 + maj) >>> 0;
-
- h = g; g = f; f = e;
- e = (d + temp1) >>> 0;
- d = c; c = b; b = a;
- a = (temp1 + temp2) >>> 0;
- }
-
- h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0; h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0;
- h4 = (h4 + e) >>> 0; h5 = (h5 + f) >>> 0; h6 = (h6 + g) >>> 0; h7 = (h7 + h) >>> 0;
- }
-
- const result = new Uint8Array(32);
- const rv = new DataView(result.buffer);
- rv.setUint32(0, h0, false); rv.setUint32(4, h1, false);
- rv.setUint32(8, h2, false); rv.setUint32(12, h3, false);
- rv.setUint32(16, h4, false); rv.setUint32(20, h5, false);
- rv.setUint32(24, h6, false); rv.setUint32(28, h7, false);
- return result;
-}
-
-/**
- * 从 passhprase + salt 派生密钥 (PBKDF2 或降级多次哈希)
- */
-async function deriveKeyBytes(salt) {
- const passBytes = new TextEncoder().encode(PASSPHRASE);
- const input = new Uint8Array(passBytes.length + salt.length);
- input.set(passBytes);
- input.set(salt, passBytes.length);
-
- if (SECURE) {
- const keyMaterial = await crypto.subtle.importKey('raw', passBytes, 'PBKDF2', false, ['deriveKey']);
- const key = await crypto.subtle.deriveKey(
- { name: 'PBKDF2', salt, iterations: PBKDF2_ITERATIONS, hash: 'SHA-256' },
- keyMaterial,
- { name: 'AES-GCM', length: 256 },
- true,
- ['encrypt', 'decrypt']
- );
- const raw = await crypto.subtle.exportKey('raw', key);
- return new Uint8Array(raw);
- }
-
- // 降级: 多次哈希模拟密钥派生
- let key = input;
- for (let i = 0; i < 10000; i++) {
- key = await sha256(key);
- }
- return key;
-}
-
-/**
- * XOR 加密/解密 (降级模式)
- */
-function xorBytes(data, keyBytes) {
- const out = new Uint8Array(data.length);
- for (let i = 0; i < data.length; i++) {
- out[i] = data[i] ^ keyBytes[i % keyBytes.length];
- }
- return out;
-}
-
-/**
- * 加密 JSON 数据 → .metona 二进制格式
- */
-export async function encryptData(data) {
- const json = new TextEncoder().encode(JSON.stringify(data));
- const salt = crypto.getRandomValues(new Uint8Array(16));
- const iv = crypto.getRandomValues(new Uint8Array(12));
- const keyBytes = await deriveKeyBytes(salt);
-
- let payload;
- if (SECURE) {
- const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['encrypt']);
- const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, json);
- payload = new Uint8Array(ciphertext);
- } else {
- // 降级: XOR 混淆,前 4 字节存原始长度
- const lenBytes = new Uint32Array([json.length]);
- const withLen = new Uint8Array(4 + json.length);
- withLen.set(new Uint8Array(lenBytes.buffer));
- withLen.set(json, 4);
- payload = xorBytes(withLen, keyBytes);
- }
-
- // 标记加密模式: SECURE=0x01, FALLBACK=0x00
- const flag = new Uint8Array([SECURE ? 1 : 0]);
-
- return new Blob([MAGIC, salt, iv, flag, payload]);
-}
-
-/**
- * 解密 .metona 文件 → JSON 对象
- */
-export async function decryptData(buffer) {
- const data = new Uint8Array(buffer);
-
- // 校验 magic
- for (let i = 0; i < 8; i++) {
- if (data[i] !== MAGIC[i]) throw new Error('不是有效的 .metona 文件');
- }
-
- const salt = data.slice(8, 24);
- const iv = data.slice(24, 36);
- const mode = data[36];
- const payload = data.slice(37);
- const keyBytes = await deriveKeyBytes(salt);
-
- let jsonBytes;
- if (mode === 1) {
- // AES-GCM
- const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['decrypt']);
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, payload);
- jsonBytes = new Uint8Array(decrypted);
- } else {
- // XOR 降级
- const withLen = xorBytes(payload, keyBytes);
- const len = new DataView(withLen.buffer).getUint32(0, true);
- jsonBytes = withLen.slice(4, 4 + len);
- }
-
- return JSON.parse(new TextDecoder().decode(jsonBytes));
-}
-
-/**
- * 检查文件是否为 .metona 格式
- */
-export function isMetonaFile(file) {
- return file.name.endsWith('.metona');
-}
diff --git a/js/document-processor.js b/js/document-processor.js
deleted file mode 100644
index e8d4cc9..0000000
--- a/js/document-processor.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * DocumentProcessor - 文档分块处理
- */
-
-/**
- * 将文本切分为语义块
- * @param {string} text - 原始文本
- * @param {object} options
- * @param {number} options.maxChunkSize - 每块最大字符数 (默认 1500)
- * @param {number} options.overlap - 块间重叠字符数 (默认 200)
- * @returns {string[]} 分块后的文本数组
- */
-export function chunkText(text, { maxChunkSize = 1500, overlap = 200 } = {}) {
- if (!text || text.trim().length === 0) return [];
-
- // 先按段落分割
- const paragraphs = text.split(/\n\s*\n/).filter(p => p.trim());
- const chunks = [];
- let current = '';
-
- for (const para of paragraphs) {
- const trimmed = para.trim();
- if (!trimmed) continue;
-
- // 单段落超过 maxChunkSize → 按句子分割
- if (trimmed.length > maxChunkSize) {
- if (current) {
- chunks.push(current.trim());
- current = '';
- }
- const sentences = splitSentences(trimmed);
- let sentenceBuf = '';
- for (const sent of sentences) {
- if ((sentenceBuf + sent).length > maxChunkSize && sentenceBuf) {
- chunks.push(sentenceBuf.trim());
- // 保留末尾 overlap
- sentenceBuf = overlap > 0
- ? sentenceBuf.slice(-overlap) + sent
- : sent;
- } else {
- sentenceBuf += sent;
- }
- }
- if (sentenceBuf.trim()) {
- current = sentenceBuf;
- }
- continue;
- }
-
- // 正常段落累积
- if ((current + '\n\n' + trimmed).length > maxChunkSize && current) {
- chunks.push(current.trim());
- // 保留末尾 overlap
- if (overlap > 0 && current.length > overlap) {
- current = current.slice(-overlap) + '\n\n' + trimmed;
- } else {
- current = trimmed;
- }
- } else {
- current = current ? current + '\n\n' + trimmed : trimmed;
- }
- }
-
- if (current.trim()) {
- chunks.push(current.trim());
- }
-
- return chunks;
-}
-
-/**
- * 按句子分割(中英文兼容)
- */
-function splitSentences(text) {
- // 中文句号、问号、感叹号 + 英文句号等
- const parts = text.split(/(?<=[。!?.!?])\s*/);
- return parts.filter(p => p.trim()).map(p => p.trim() + ' ');
-}
-
-/**
- * 从文件内容提取文档信息
- * @param {string} content - 文件内容
- * @param {string} filename - 文件名
- * @returns {{ text: string, filename: string }}
- */
-export function extractDocument(content, filename) {
- return {
- text: content,
- filename
- };
-}
-
-/**
- * 生成文档块的元数据
- */
-export function createChunkMetadata(chunk, index, docId, filename) {
- return {
- id: `${docId}_chunk_${index}`,
- docId,
- filename,
- chunkIndex: index,
- text: chunk,
- charCount: chunk.length
- };
-}
diff --git a/js/lib/marked.esm.js b/js/lib/marked.esm.js
deleted file mode 100644
index d07059e..0000000
--- a/js/lib/marked.esm.js
+++ /dev/null
@@ -1,2580 +0,0 @@
-/**
- * marked v15.0.7 - a markdown parser
- * Copyright (c) 2011-2025, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/markedjs/marked
- */
-
-/**
- * DO NOT EDIT THIS FILE
- * The code in this file is generated from files in ./src/
- */
-
-/**
- * Gets the original marked default options.
- */
-function _getDefaults() {
- return {
- async: false,
- breaks: false,
- extensions: null,
- gfm: true,
- hooks: null,
- pedantic: false,
- renderer: null,
- silent: false,
- tokenizer: null,
- walkTokens: null,
- };
-}
-let _defaults = _getDefaults();
-function changeDefaults(newDefaults) {
- _defaults = newDefaults;
-}
-
-const noopTest = { exec: () => null };
-function edit(regex, opt = '') {
- let source = typeof regex === 'string' ? regex : regex.source;
- const obj = {
- replace: (name, val) => {
- let valSource = typeof val === 'string' ? val : val.source;
- valSource = valSource.replace(other.caret, '$1');
- source = source.replace(name, valSource);
- return obj;
- },
- getRegex: () => {
- return new RegExp(source, opt);
- },
- };
- return obj;
-}
-const other = {
- codeRemoveIndent: /^(?: {1,4}| {0,3}\t)/gm,
- outputLinkReplace: /\\([\[\]])/g,
- indentCodeCompensation: /^(\s+)(?:```)/,
- beginningSpace: /^\s+/,
- endingHash: /#$/,
- startingSpaceChar: /^ /,
- endingSpaceChar: / $/,
- nonSpaceChar: /[^ ]/,
- newLineCharGlobal: /\n/g,
- tabCharGlobal: /\t/g,
- multipleSpaceGlobal: /\s+/g,
- blankLine: /^[ \t]*$/,
- doubleBlankLine: /\n[ \t]*\n[ \t]*$/,
- blockquoteStart: /^ {0,3}>/,
- blockquoteSetextReplace: /\n {0,3}((?:=+|-+) *)(?=\n|$)/g,
- blockquoteSetextReplace2: /^ {0,3}>[ \t]?/gm,
- listReplaceTabs: /^\t+/,
- listReplaceNesting: /^ {1,4}(?=( {4})*[^ ])/g,
- listIsTask: /^\[[ xX]\] /,
- listReplaceTask: /^\[[ xX]\] +/,
- anyLine: /\n.*\n/,
- hrefBrackets: /^<(.*)>$/,
- tableDelimiter: /[:|]/,
- tableAlignChars: /^\||\| *$/g,
- tableRowBlankLine: /\n[ \t]*$/,
- tableAlignRight: /^ *-+: *$/,
- tableAlignCenter: /^ *:-+: *$/,
- tableAlignLeft: /^ *:-+ *$/,
- startATag: /^/i,
- startPreScriptTag: /^<(pre|code|kbd|script)(\s|>)/i,
- endPreScriptTag: /^<\/(pre|code|kbd|script)(\s|>)/i,
- startAngleBracket: /^,
- endAngleBracket: />$/,
- pedanticHrefTitle: /^([^'"]*[^\s])\s+(['"])(.*)\2/,
- unicodeAlphaNumeric: /[\p{L}\p{N}]/u,
- escapeTest: /[&<>"']/,
- escapeReplace: /[&<>"']/g,
- escapeTestNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,
- escapeReplaceNoEncode: /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,
- unescapeTest: /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig,
- caret: /(^|[^\[])\^/g,
- percentDecode: /%25/g,
- findPipe: /\|/g,
- splitPipe: / \|/,
- slashPipe: /\\\|/g,
- carriageReturn: /\r\n|\r/g,
- spaceLine: /^ +$/gm,
- notSpaceStart: /^\S*/,
- endingNewline: /\n$/,
- listItemRegex: (bull) => new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`),
- nextBulletRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),
- hrRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),
- fencesBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`),
- headingBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`),
- htmlBeginRegex: (indent) => new RegExp(`^ {0,${Math.min(3, indent - 1)}}<(?:[a-z].*>|!--)`, 'i'),
-};
-/**
- * Block-Level Grammar
- */
-const newline = /^(?:[ \t]*(?:\n|$))+/;
-const blockCode = /^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/;
-const fences = /^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/;
-const hr = /^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/;
-const heading = /^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/;
-const bullet = /(?:[*+-]|\d{1,9}[.)])/;
-const lheadingCore = /^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/;
-const lheading = edit(lheadingCore)
- .replace(/bull/g, bullet) // lists can interrupt
- .replace(/blockCode/g, /(?: {4}| {0,3}\t)/) // indented code blocks can interrupt
- .replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt
- .replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt
- .replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt
- .replace(/html/g, / {0,3}<[^\n>]+>\n/) // block html can interrupt
- .replace(/\|table/g, '') // table not in commonmark
- .getRegex();
-const lheadingGfm = edit(lheadingCore)
- .replace(/bull/g, bullet) // lists can interrupt
- .replace(/blockCode/g, /(?: {4}| {0,3}\t)/) // indented code blocks can interrupt
- .replace(/fences/g, / {0,3}(?:`{3,}|~{3,})/) // fenced code blocks can interrupt
- .replace(/blockquote/g, / {0,3}>/) // blockquote can interrupt
- .replace(/heading/g, / {0,3}#{1,6}/) // ATX heading can interrupt
- .replace(/html/g, / {0,3}<[^\n>]+>\n/) // block html can interrupt
- .replace(/table/g, / {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/) // table can interrupt
- .getRegex();
-const _paragraph = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/;
-const blockText = /^[^\n]+/;
-const _blockLabel = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
-const def = edit(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/)
- .replace('label', _blockLabel)
- .replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
- .getRegex();
-const list = edit(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/)
- .replace(/bull/g, bullet)
- .getRegex();
-const _tag = 'address|article|aside|base|basefont|blockquote|body|caption'
- + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
- + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
- + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
- + '|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title'
- + '|tr|track|ul';
-const _comment = /|$))/;
-const html = edit('^ {0,3}(?:' // optional indentation
- + '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)' // (1)
- + '|comment[^\\n]*(\\n+|$)' // (2)
- + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
- + '|\\n*|$)' // (4)
- + '|\\n*|$)' // (5)
- + '|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (6)
- + '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) open tag
- + '|(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) closing tag
- + ')', 'i')
- .replace('comment', _comment)
- .replace('tag', _tag)
- .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
- .getRegex();
-const paragraph = edit(_paragraph)
- .replace('hr', hr)
- .replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
- .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
- .replace('|table', '')
- .replace('blockquote', ' {0,3}>')
- .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
- .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
- .replace('html', '?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
- .replace('tag', _tag) // pars can be interrupted by type (6) html blocks
- .getRegex();
-const blockquote = edit(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/)
- .replace('paragraph', paragraph)
- .getRegex();
-/**
- * Normal Block Grammar
- */
-const blockNormal = {
- blockquote,
- code: blockCode,
- def,
- fences,
- heading,
- hr,
- html,
- lheading,
- list,
- newline,
- paragraph,
- table: noopTest,
- text: blockText,
-};
-/**
- * GFM Block Grammar
- */
-const gfmTable = edit('^ *([^\\n ].*)\\n' // Header
- + ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align
- + '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)') // Cells
- .replace('hr', hr)
- .replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
- .replace('blockquote', ' {0,3}>')
- .replace('code', '(?: {4}| {0,3}\t)[^\\n]')
- .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
- .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
- .replace('html', '?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
- .replace('tag', _tag) // tables can be interrupted by type (6) html blocks
- .getRegex();
-const blockGfm = {
- ...blockNormal,
- lheading: lheadingGfm,
- table: gfmTable,
- paragraph: edit(_paragraph)
- .replace('hr', hr)
- .replace('heading', ' {0,3}#{1,6}(?:\\s|$)')
- .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs
- .replace('table', gfmTable) // interrupt paragraphs with table
- .replace('blockquote', ' {0,3}>')
- .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
- .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
- .replace('html', '?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
- .replace('tag', _tag) // pars can be interrupted by type (6) html blocks
- .getRegex(),
-};
-/**
- * Pedantic grammar (original John Gruber's loose markdown specification)
- */
-const blockPedantic = {
- ...blockNormal,
- html: edit('^ *(?:comment *(?:\\n|\\s*$)'
- + '|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)' // closed tag
- + '| \\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
- .replace('comment', _comment)
- .replace(/tag/g, '(?!(?:'
- + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
- + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
- + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
- .getRegex(),
- def: /^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
- heading: /^(#{1,6})(.*)(?:\n+|$)/,
- fences: noopTest, // fences not supported
- lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,
- paragraph: edit(_paragraph)
- .replace('hr', hr)
- .replace('heading', ' *#{1,6} *[^\n]')
- .replace('lheading', lheading)
- .replace('|table', '')
- .replace('blockquote', ' {0,3}>')
- .replace('|fences', '')
- .replace('|list', '')
- .replace('|html', '')
- .replace('|tag', '')
- .getRegex(),
-};
-/**
- * Inline-Level Grammar
- */
-const escape$1 = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
-const inlineCode = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
-const br = /^( {2,}|\\)\n(?!\s*$)/;
-const inlineText = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\
-const blockSkip = /\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g;
-const emStrongLDelimCore = /^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/;
-const emStrongLDelim = edit(emStrongLDelimCore, 'u')
- .replace(/punct/g, _punctuation)
- .getRegex();
-const emStrongLDelimGfm = edit(emStrongLDelimCore, 'u')
- .replace(/punct/g, _punctuationGfmStrongEm)
- .getRegex();
-const emStrongRDelimAstCore = '^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)' // Skip orphan inside strong
- + '|[^*]+(?=[^*])' // Consume to delim
- + '|(?!\\*)punct(\\*+)(?=[\\s]|$)' // (1) #*** can only be a Right Delimiter
- + '|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)' // (2) a***#, a*** can only be a Right Delimiter
- + '|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)' // (3) #***a, ***a can only be Left Delimiter
- + '|[\\s](\\*+)(?!\\*)(?=punct)' // (4) ***# can only be Left Delimiter
- + '|(?!\\*)punct(\\*+)(?!\\*)(?=punct)' // (5) #***# can be either Left or Right Delimiter
- + '|notPunctSpace(\\*+)(?=notPunctSpace)'; // (6) a***a can be either Left or Right Delimiter
-const emStrongRDelimAst = edit(emStrongRDelimAstCore, 'gu')
- .replace(/notPunctSpace/g, _notPunctuationOrSpace)
- .replace(/punctSpace/g, _punctuationOrSpace)
- .replace(/punct/g, _punctuation)
- .getRegex();
-const emStrongRDelimAstGfm = edit(emStrongRDelimAstCore, 'gu')
- .replace(/notPunctSpace/g, _notPunctuationOrSpaceGfmStrongEm)
- .replace(/punctSpace/g, _punctuationOrSpaceGfmStrongEm)
- .replace(/punct/g, _punctuationGfmStrongEm)
- .getRegex();
-// (6) Not allowed for _
-const emStrongRDelimUnd = edit('^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)' // Skip orphan inside strong
- + '|[^_]+(?=[^_])' // Consume to delim
- + '|(?!_)punct(_+)(?=[\\s]|$)' // (1) #___ can only be a Right Delimiter
- + '|notPunctSpace(_+)(?!_)(?=punctSpace|$)' // (2) a___#, a___ can only be a Right Delimiter
- + '|(?!_)punctSpace(_+)(?=notPunctSpace)' // (3) #___a, ___a can only be Left Delimiter
- + '|[\\s](_+)(?!_)(?=punct)' // (4) ___# can only be Left Delimiter
- + '|(?!_)punct(_+)(?!_)(?=punct)', 'gu') // (5) #___# can be either Left or Right Delimiter
- .replace(/notPunctSpace/g, _notPunctuationOrSpace)
- .replace(/punctSpace/g, _punctuationOrSpace)
- .replace(/punct/g, _punctuation)
- .getRegex();
-const anyPunctuation = edit(/\\(punct)/, 'gu')
- .replace(/punct/g, _punctuation)
- .getRegex();
-const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/)
- .replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/)
- .replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/)
- .getRegex();
-const _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex();
-const tag = edit('^comment'
- + '|^[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
- + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
- + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g.
- + '|^' // declaration, e.g.
- + '|^') // CDATA section
- .replace('comment', _inlineComment)
- .replace('attribute', /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/)
- .getRegex();
-const _inlineLabel = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
-const link = edit(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/)
- .replace('label', _inlineLabel)
- .replace('href', /<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/)
- .replace('title', /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/)
- .getRegex();
-const reflink = edit(/^!?\[(label)\]\[(ref)\]/)
- .replace('label', _inlineLabel)
- .replace('ref', _blockLabel)
- .getRegex();
-const nolink = edit(/^!?\[(ref)\](?:\[\])?/)
- .replace('ref', _blockLabel)
- .getRegex();
-const reflinkSearch = edit('reflink|nolink(?!\\()', 'g')
- .replace('reflink', reflink)
- .replace('nolink', nolink)
- .getRegex();
-/**
- * Normal Inline Grammar
- */
-const inlineNormal = {
- _backpedal: noopTest, // only used for GFM url
- anyPunctuation,
- autolink,
- blockSkip,
- br,
- code: inlineCode,
- del: noopTest,
- emStrongLDelim,
- emStrongRDelimAst,
- emStrongRDelimUnd,
- escape: escape$1,
- link,
- nolink,
- punctuation,
- reflink,
- reflinkSearch,
- tag,
- text: inlineText,
- url: noopTest,
-};
-/**
- * Pedantic Inline Grammar
- */
-const inlinePedantic = {
- ...inlineNormal,
- link: edit(/^!?\[(label)\]\((.*?)\)/)
- .replace('label', _inlineLabel)
- .getRegex(),
- reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
- .replace('label', _inlineLabel)
- .getRegex(),
-};
-/**
- * GFM Inline Grammar
- */
-const inlineGfm = {
- ...inlineNormal,
- emStrongRDelimAst: emStrongRDelimAstGfm,
- emStrongLDelim: emStrongLDelimGfm,
- url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, 'i')
- .replace('email', /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/)
- .getRegex(),
- _backpedal: /(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,
- del: /^(~~?)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/,
- text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\': '>',
- '"': '"',
- "'": ''',
-};
-const getEscapeReplacement = (ch) => escapeReplacements[ch];
-function escape(html, encode) {
- if (encode) {
- if (other.escapeTest.test(html)) {
- return html.replace(other.escapeReplace, getEscapeReplacement);
- }
- }
- else {
- if (other.escapeTestNoEncode.test(html)) {
- return html.replace(other.escapeReplaceNoEncode, getEscapeReplacement);
- }
- }
- return html;
-}
-function cleanUrl(href) {
- try {
- href = encodeURI(href).replace(other.percentDecode, '%');
- }
- catch {
- return null;
- }
- return href;
-}
-function splitCells(tableRow, count) {
- // ensure that every cell-delimiting pipe has a space
- // before it to distinguish it from an escaped pipe
- const row = tableRow.replace(other.findPipe, (match, offset, str) => {
- let escaped = false;
- let curr = offset;
- while (--curr >= 0 && str[curr] === '\\')
- escaped = !escaped;
- if (escaped) {
- // odd number of slashes means | is escaped
- // so we leave it alone
- return '|';
- }
- else {
- // add space before unescaped |
- return ' |';
- }
- }), cells = row.split(other.splitPipe);
- let i = 0;
- // First/last cell in a row cannot be empty if it has no leading/trailing pipe
- if (!cells[0].trim()) {
- cells.shift();
- }
- if (cells.length > 0 && !cells.at(-1)?.trim()) {
- cells.pop();
- }
- if (count) {
- if (cells.length > count) {
- cells.splice(count);
- }
- else {
- while (cells.length < count)
- cells.push('');
- }
- }
- for (; i < cells.length; i++) {
- // leading or trailing whitespace is ignored per the gfm spec
- cells[i] = cells[i].trim().replace(other.slashPipe, '|');
- }
- return cells;
-}
-/**
- * Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
- * /c*$/ is vulnerable to REDOS.
- *
- * @param str
- * @param c
- * @param invert Remove suffix of non-c chars instead. Default falsey.
- */
-function rtrim(str, c, invert) {
- const l = str.length;
- if (l === 0) {
- return '';
- }
- // Length of suffix matching the invert condition.
- let suffLen = 0;
- // Step left until we fail to match the invert condition.
- while (suffLen < l) {
- const currChar = str.charAt(l - suffLen - 1);
- if (currChar === c && true) {
- suffLen++;
- }
- else {
- break;
- }
- }
- return str.slice(0, l - suffLen);
-}
-function findClosingBracket(str, b) {
- if (str.indexOf(b[1]) === -1) {
- return -1;
- }
- let level = 0;
- for (let i = 0; i < str.length; i++) {
- if (str[i] === '\\') {
- i++;
- }
- else if (str[i] === b[0]) {
- level++;
- }
- else if (str[i] === b[1]) {
- level--;
- if (level < 0) {
- return i;
- }
- }
- }
- return -1;
-}
-
-function outputLink(cap, link, raw, lexer, rules) {
- const href = link.href;
- const title = link.title || null;
- const text = cap[1].replace(rules.other.outputLinkReplace, '$1');
- if (cap[0].charAt(0) !== '!') {
- lexer.state.inLink = true;
- const token = {
- type: 'link',
- raw,
- href,
- title,
- text,
- tokens: lexer.inlineTokens(text),
- };
- lexer.state.inLink = false;
- return token;
- }
- return {
- type: 'image',
- raw,
- href,
- title,
- text,
- };
-}
-function indentCodeCompensation(raw, text, rules) {
- const matchIndentToCode = raw.match(rules.other.indentCodeCompensation);
- if (matchIndentToCode === null) {
- return text;
- }
- const indentToCode = matchIndentToCode[1];
- return text
- .split('\n')
- .map(node => {
- const matchIndentInNode = node.match(rules.other.beginningSpace);
- if (matchIndentInNode === null) {
- return node;
- }
- const [indentInNode] = matchIndentInNode;
- if (indentInNode.length >= indentToCode.length) {
- return node.slice(indentToCode.length);
- }
- return node;
- })
- .join('\n');
-}
-/**
- * Tokenizer
- */
-class _Tokenizer {
- options;
- rules; // set by the lexer
- lexer; // set by the lexer
- constructor(options) {
- this.options = options || _defaults;
- }
- space(src) {
- const cap = this.rules.block.newline.exec(src);
- if (cap && cap[0].length > 0) {
- return {
- type: 'space',
- raw: cap[0],
- };
- }
- }
- code(src) {
- const cap = this.rules.block.code.exec(src);
- if (cap) {
- const text = cap[0].replace(this.rules.other.codeRemoveIndent, '');
- return {
- type: 'code',
- raw: cap[0],
- codeBlockStyle: 'indented',
- text: !this.options.pedantic
- ? rtrim(text, '\n')
- : text,
- };
- }
- }
- fences(src) {
- const cap = this.rules.block.fences.exec(src);
- if (cap) {
- const raw = cap[0];
- const text = indentCodeCompensation(raw, cap[3] || '', this.rules);
- return {
- type: 'code',
- raw,
- lang: cap[2] ? cap[2].trim().replace(this.rules.inline.anyPunctuation, '$1') : cap[2],
- text,
- };
- }
- }
- heading(src) {
- const cap = this.rules.block.heading.exec(src);
- if (cap) {
- let text = cap[2].trim();
- // remove trailing #s
- if (this.rules.other.endingHash.test(text)) {
- const trimmed = rtrim(text, '#');
- if (this.options.pedantic) {
- text = trimmed.trim();
- }
- else if (!trimmed || this.rules.other.endingSpaceChar.test(trimmed)) {
- // CommonMark requires space before trailing #s
- text = trimmed.trim();
- }
- }
- return {
- type: 'heading',
- raw: cap[0],
- depth: cap[1].length,
- text,
- tokens: this.lexer.inline(text),
- };
- }
- }
- hr(src) {
- const cap = this.rules.block.hr.exec(src);
- if (cap) {
- return {
- type: 'hr',
- raw: rtrim(cap[0], '\n'),
- };
- }
- }
- blockquote(src) {
- const cap = this.rules.block.blockquote.exec(src);
- if (cap) {
- let lines = rtrim(cap[0], '\n').split('\n');
- let raw = '';
- let text = '';
- const tokens = [];
- while (lines.length > 0) {
- let inBlockquote = false;
- const currentLines = [];
- let i;
- for (i = 0; i < lines.length; i++) {
- // get lines up to a continuation
- if (this.rules.other.blockquoteStart.test(lines[i])) {
- currentLines.push(lines[i]);
- inBlockquote = true;
- }
- else if (!inBlockquote) {
- currentLines.push(lines[i]);
- }
- else {
- break;
- }
- }
- lines = lines.slice(i);
- const currentRaw = currentLines.join('\n');
- const currentText = currentRaw
- // precede setext continuation with 4 spaces so it isn't a setext
- .replace(this.rules.other.blockquoteSetextReplace, '\n $1')
- .replace(this.rules.other.blockquoteSetextReplace2, '');
- raw = raw ? `${raw}\n${currentRaw}` : currentRaw;
- text = text ? `${text}\n${currentText}` : currentText;
- // parse blockquote lines as top level tokens
- // merge paragraphs if this is a continuation
- const top = this.lexer.state.top;
- this.lexer.state.top = true;
- this.lexer.blockTokens(currentText, tokens, true);
- this.lexer.state.top = top;
- // if there is no continuation then we are done
- if (lines.length === 0) {
- break;
- }
- const lastToken = tokens.at(-1);
- if (lastToken?.type === 'code') {
- // blockquote continuation cannot be preceded by a code block
- break;
- }
- else if (lastToken?.type === 'blockquote') {
- // include continuation in nested blockquote
- const oldToken = lastToken;
- const newText = oldToken.raw + '\n' + lines.join('\n');
- const newToken = this.blockquote(newText);
- tokens[tokens.length - 1] = newToken;
- raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw;
- text = text.substring(0, text.length - oldToken.text.length) + newToken.text;
- break;
- }
- else if (lastToken?.type === 'list') {
- // include continuation in nested list
- const oldToken = lastToken;
- const newText = oldToken.raw + '\n' + lines.join('\n');
- const newToken = this.list(newText);
- tokens[tokens.length - 1] = newToken;
- raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw;
- text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw;
- lines = newText.substring(tokens.at(-1).raw.length).split('\n');
- continue;
- }
- }
- return {
- type: 'blockquote',
- raw,
- tokens,
- text,
- };
- }
- }
- list(src) {
- let cap = this.rules.block.list.exec(src);
- if (cap) {
- let bull = cap[1].trim();
- const isordered = bull.length > 1;
- const list = {
- type: 'list',
- raw: '',
- ordered: isordered,
- start: isordered ? +bull.slice(0, -1) : '',
- loose: false,
- items: [],
- };
- bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
- if (this.options.pedantic) {
- bull = isordered ? bull : '[*+-]';
- }
- // Get next list item
- const itemRegex = this.rules.other.listItemRegex(bull);
- let endsWithBlankLine = false;
- // Check if current bullet point can start a new List Item
- while (src) {
- let endEarly = false;
- let raw = '';
- let itemContents = '';
- if (!(cap = itemRegex.exec(src))) {
- break;
- }
- if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)
- break;
- }
- raw = cap[0];
- src = src.substring(raw.length);
- let line = cap[2].split('\n', 1)[0].replace(this.rules.other.listReplaceTabs, (t) => ' '.repeat(3 * t.length));
- let nextLine = src.split('\n', 1)[0];
- let blankLine = !line.trim();
- let indent = 0;
- if (this.options.pedantic) {
- indent = 2;
- itemContents = line.trimStart();
- }
- else if (blankLine) {
- indent = cap[1].length + 1;
- }
- else {
- indent = cap[2].search(this.rules.other.nonSpaceChar); // Find first non-space char
- indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
- itemContents = line.slice(indent);
- indent += cap[1].length;
- }
- if (blankLine && this.rules.other.blankLine.test(nextLine)) { // Items begin with at most one blank line
- raw += nextLine + '\n';
- src = src.substring(nextLine.length + 1);
- endEarly = true;
- }
- if (!endEarly) {
- const nextBulletRegex = this.rules.other.nextBulletRegex(indent);
- const hrRegex = this.rules.other.hrRegex(indent);
- const fencesBeginRegex = this.rules.other.fencesBeginRegex(indent);
- const headingBeginRegex = this.rules.other.headingBeginRegex(indent);
- const htmlBeginRegex = this.rules.other.htmlBeginRegex(indent);
- // Check if following lines should be included in List Item
- while (src) {
- const rawLine = src.split('\n', 1)[0];
- let nextLineWithoutTabs;
- nextLine = rawLine;
- // Re-align to follow commonmark nesting rules
- if (this.options.pedantic) {
- nextLine = nextLine.replace(this.rules.other.listReplaceNesting, ' ');
- nextLineWithoutTabs = nextLine;
- }
- else {
- nextLineWithoutTabs = nextLine.replace(this.rules.other.tabCharGlobal, ' ');
- }
- // End list item if found code fences
- if (fencesBeginRegex.test(nextLine)) {
- break;
- }
- // End list item if found start of new heading
- if (headingBeginRegex.test(nextLine)) {
- break;
- }
- // End list item if found start of html block
- if (htmlBeginRegex.test(nextLine)) {
- break;
- }
- // End list item if found start of new bullet
- if (nextBulletRegex.test(nextLine)) {
- break;
- }
- // Horizontal rule found
- if (hrRegex.test(nextLine)) {
- break;
- }
- if (nextLineWithoutTabs.search(this.rules.other.nonSpaceChar) >= indent || !nextLine.trim()) { // Dedent if possible
- itemContents += '\n' + nextLineWithoutTabs.slice(indent);
- }
- else {
- // not enough indentation
- if (blankLine) {
- break;
- }
- // paragraph continuation unless last line was a different block level element
- if (line.replace(this.rules.other.tabCharGlobal, ' ').search(this.rules.other.nonSpaceChar) >= 4) { // indented code block
- break;
- }
- if (fencesBeginRegex.test(line)) {
- break;
- }
- if (headingBeginRegex.test(line)) {
- break;
- }
- if (hrRegex.test(line)) {
- break;
- }
- itemContents += '\n' + nextLine;
- }
- if (!blankLine && !nextLine.trim()) { // Check if current line is blank
- blankLine = true;
- }
- raw += rawLine + '\n';
- src = src.substring(rawLine.length + 1);
- line = nextLineWithoutTabs.slice(indent);
- }
- }
- if (!list.loose) {
- // If the previous item ended with a blank line, the list is loose
- if (endsWithBlankLine) {
- list.loose = true;
- }
- else if (this.rules.other.doubleBlankLine.test(raw)) {
- endsWithBlankLine = true;
- }
- }
- let istask = null;
- let ischecked;
- // Check for task list items
- if (this.options.gfm) {
- istask = this.rules.other.listIsTask.exec(itemContents);
- if (istask) {
- ischecked = istask[0] !== '[ ] ';
- itemContents = itemContents.replace(this.rules.other.listReplaceTask, '');
- }
- }
- list.items.push({
- type: 'list_item',
- raw,
- task: !!istask,
- checked: ischecked,
- loose: false,
- text: itemContents,
- tokens: [],
- });
- list.raw += raw;
- }
- // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
- const lastItem = list.items.at(-1);
- if (lastItem) {
- lastItem.raw = lastItem.raw.trimEnd();
- lastItem.text = lastItem.text.trimEnd();
- }
- else {
- // not a list since there were no items
- return;
- }
- list.raw = list.raw.trimEnd();
- // Item child tokens handled here at end because we needed to have the final item to trim it first
- for (let i = 0; i < list.items.length; i++) {
- this.lexer.state.top = false;
- list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
- if (!list.loose) {
- // Check if list should be loose
- const spacers = list.items[i].tokens.filter(t => t.type === 'space');
- const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => this.rules.other.anyLine.test(t.raw));
- list.loose = hasMultipleLineBreaks;
- }
- }
- // Set all items to loose if list is loose
- if (list.loose) {
- for (let i = 0; i < list.items.length; i++) {
- list.items[i].loose = true;
- }
- }
- return list;
- }
- }
- html(src) {
- const cap = this.rules.block.html.exec(src);
- if (cap) {
- const token = {
- type: 'html',
- block: true,
- raw: cap[0],
- pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
- text: cap[0],
- };
- return token;
- }
- }
- def(src) {
- const cap = this.rules.block.def.exec(src);
- if (cap) {
- const tag = cap[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal, ' ');
- const href = cap[2] ? cap[2].replace(this.rules.other.hrefBrackets, '$1').replace(this.rules.inline.anyPunctuation, '$1') : '';
- const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline.anyPunctuation, '$1') : cap[3];
- return {
- type: 'def',
- tag,
- raw: cap[0],
- href,
- title,
- };
- }
- }
- table(src) {
- const cap = this.rules.block.table.exec(src);
- if (!cap) {
- return;
- }
- if (!this.rules.other.tableDelimiter.test(cap[2])) {
- // delimiter row must have a pipe (|) or colon (:) otherwise it is a setext heading
- return;
- }
- const headers = splitCells(cap[1]);
- const aligns = cap[2].replace(this.rules.other.tableAlignChars, '').split('|');
- const rows = cap[3]?.trim() ? cap[3].replace(this.rules.other.tableRowBlankLine, '').split('\n') : [];
- const item = {
- type: 'table',
- raw: cap[0],
- header: [],
- align: [],
- rows: [],
- };
- if (headers.length !== aligns.length) {
- // header and align columns must be equal, rows can be different.
- return;
- }
- for (const align of aligns) {
- if (this.rules.other.tableAlignRight.test(align)) {
- item.align.push('right');
- }
- else if (this.rules.other.tableAlignCenter.test(align)) {
- item.align.push('center');
- }
- else if (this.rules.other.tableAlignLeft.test(align)) {
- item.align.push('left');
- }
- else {
- item.align.push(null);
- }
- }
- for (let i = 0; i < headers.length; i++) {
- item.header.push({
- text: headers[i],
- tokens: this.lexer.inline(headers[i]),
- header: true,
- align: item.align[i],
- });
- }
- for (const row of rows) {
- item.rows.push(splitCells(row, item.header.length).map((cell, i) => {
- return {
- text: cell,
- tokens: this.lexer.inline(cell),
- header: false,
- align: item.align[i],
- };
- }));
- }
- return item;
- }
- lheading(src) {
- const cap = this.rules.block.lheading.exec(src);
- if (cap) {
- return {
- type: 'heading',
- raw: cap[0],
- depth: cap[2].charAt(0) === '=' ? 1 : 2,
- text: cap[1],
- tokens: this.lexer.inline(cap[1]),
- };
- }
- }
- paragraph(src) {
- const cap = this.rules.block.paragraph.exec(src);
- if (cap) {
- const text = cap[1].charAt(cap[1].length - 1) === '\n'
- ? cap[1].slice(0, -1)
- : cap[1];
- return {
- type: 'paragraph',
- raw: cap[0],
- text,
- tokens: this.lexer.inline(text),
- };
- }
- }
- text(src) {
- const cap = this.rules.block.text.exec(src);
- if (cap) {
- return {
- type: 'text',
- raw: cap[0],
- text: cap[0],
- tokens: this.lexer.inline(cap[0]),
- };
- }
- }
- escape(src) {
- const cap = this.rules.inline.escape.exec(src);
- if (cap) {
- return {
- type: 'escape',
- raw: cap[0],
- text: cap[1],
- };
- }
- }
- tag(src) {
- const cap = this.rules.inline.tag.exec(src);
- if (cap) {
- if (!this.lexer.state.inLink && this.rules.other.startATag.test(cap[0])) {
- this.lexer.state.inLink = true;
- }
- else if (this.lexer.state.inLink && this.rules.other.endATag.test(cap[0])) {
- this.lexer.state.inLink = false;
- }
- if (!this.lexer.state.inRawBlock && this.rules.other.startPreScriptTag.test(cap[0])) {
- this.lexer.state.inRawBlock = true;
- }
- else if (this.lexer.state.inRawBlock && this.rules.other.endPreScriptTag.test(cap[0])) {
- this.lexer.state.inRawBlock = false;
- }
- return {
- type: 'html',
- raw: cap[0],
- inLink: this.lexer.state.inLink,
- inRawBlock: this.lexer.state.inRawBlock,
- block: false,
- text: cap[0],
- };
- }
- }
- link(src) {
- const cap = this.rules.inline.link.exec(src);
- if (cap) {
- const trimmedUrl = cap[2].trim();
- if (!this.options.pedantic && this.rules.other.startAngleBracket.test(trimmedUrl)) {
- // commonmark requires matching angle brackets
- if (!(this.rules.other.endAngleBracket.test(trimmedUrl))) {
- return;
- }
- // ending angle bracket cannot be escaped
- const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
- if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
- return;
- }
- }
- else {
- // find closing parenthesis
- const lastParenIndex = findClosingBracket(cap[2], '()');
- if (lastParenIndex > -1) {
- const start = cap[0].indexOf('!') === 0 ? 5 : 4;
- const linkLen = start + cap[1].length + lastParenIndex;
- cap[2] = cap[2].substring(0, lastParenIndex);
- cap[0] = cap[0].substring(0, linkLen).trim();
- cap[3] = '';
- }
- }
- let href = cap[2];
- let title = '';
- if (this.options.pedantic) {
- // split pedantic href and title
- const link = this.rules.other.pedanticHrefTitle.exec(href);
- if (link) {
- href = link[1];
- title = link[3];
- }
- }
- else {
- title = cap[3] ? cap[3].slice(1, -1) : '';
- }
- href = href.trim();
- if (this.rules.other.startAngleBracket.test(href)) {
- if (this.options.pedantic && !(this.rules.other.endAngleBracket.test(trimmedUrl))) {
- // pedantic allows starting angle bracket without ending angle bracket
- href = href.slice(1);
- }
- else {
- href = href.slice(1, -1);
- }
- }
- return outputLink(cap, {
- href: href ? href.replace(this.rules.inline.anyPunctuation, '$1') : href,
- title: title ? title.replace(this.rules.inline.anyPunctuation, '$1') : title,
- }, cap[0], this.lexer, this.rules);
- }
- }
- reflink(src, links) {
- let cap;
- if ((cap = this.rules.inline.reflink.exec(src))
- || (cap = this.rules.inline.nolink.exec(src))) {
- const linkString = (cap[2] || cap[1]).replace(this.rules.other.multipleSpaceGlobal, ' ');
- const link = links[linkString.toLowerCase()];
- if (!link) {
- const text = cap[0].charAt(0);
- return {
- type: 'text',
- raw: text,
- text,
- };
- }
- return outputLink(cap, link, cap[0], this.lexer, this.rules);
- }
- }
- emStrong(src, maskedSrc, prevChar = '') {
- let match = this.rules.inline.emStrongLDelim.exec(src);
- if (!match)
- return;
- // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
- if (match[3] && prevChar.match(this.rules.other.unicodeAlphaNumeric))
- return;
- const nextChar = match[1] || match[2] || '';
- if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
- // unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
- const lLength = [...match[0]].length - 1;
- let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
- const endReg = match[0][0] === '*' ? this.rules.inline.emStrongRDelimAst : this.rules.inline.emStrongRDelimUnd;
- endReg.lastIndex = 0;
- // Clip maskedSrc to same section of string as src (move to lexer?)
- maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
- while ((match = endReg.exec(maskedSrc)) != null) {
- rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
- if (!rDelim)
- continue; // skip single * in __abc*abc__
- rLength = [...rDelim].length;
- if (match[3] || match[4]) { // found another Left Delim
- delimTotal += rLength;
- continue;
- }
- else if (match[5] || match[6]) { // either Left or Right Delim
- if (lLength % 3 && !((lLength + rLength) % 3)) {
- midDelimTotal += rLength;
- continue; // CommonMark Emphasis Rules 9-10
- }
- }
- delimTotal -= rLength;
- if (delimTotal > 0)
- continue; // Haven't found enough closing delimiters
- // Remove extra characters. *a*** -> *a*
- rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
- // char length can be >1 for unicode characters;
- const lastCharLength = [...match[0]][0].length;
- const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
- // Create `em` if smallest delimiter has odd char count. *a***
- if (Math.min(lLength, rLength) % 2) {
- const text = raw.slice(1, -1);
- return {
- type: 'em',
- raw,
- text,
- tokens: this.lexer.inlineTokens(text),
- };
- }
- // Create 'strong' if smallest delimiter has even char count. **a***
- const text = raw.slice(2, -2);
- return {
- type: 'strong',
- raw,
- text,
- tokens: this.lexer.inlineTokens(text),
- };
- }
- }
- }
- codespan(src) {
- const cap = this.rules.inline.code.exec(src);
- if (cap) {
- let text = cap[2].replace(this.rules.other.newLineCharGlobal, ' ');
- const hasNonSpaceChars = this.rules.other.nonSpaceChar.test(text);
- const hasSpaceCharsOnBothEnds = this.rules.other.startingSpaceChar.test(text) && this.rules.other.endingSpaceChar.test(text);
- if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
- text = text.substring(1, text.length - 1);
- }
- return {
- type: 'codespan',
- raw: cap[0],
- text,
- };
- }
- }
- br(src) {
- const cap = this.rules.inline.br.exec(src);
- if (cap) {
- return {
- type: 'br',
- raw: cap[0],
- };
- }
- }
- del(src) {
- const cap = this.rules.inline.del.exec(src);
- if (cap) {
- return {
- type: 'del',
- raw: cap[0],
- text: cap[2],
- tokens: this.lexer.inlineTokens(cap[2]),
- };
- }
- }
- autolink(src) {
- const cap = this.rules.inline.autolink.exec(src);
- if (cap) {
- let text, href;
- if (cap[2] === '@') {
- text = cap[1];
- href = 'mailto:' + text;
- }
- else {
- text = cap[1];
- href = text;
- }
- return {
- type: 'link',
- raw: cap[0],
- text,
- href,
- tokens: [
- {
- type: 'text',
- raw: text,
- text,
- },
- ],
- };
- }
- }
- url(src) {
- let cap;
- if (cap = this.rules.inline.url.exec(src)) {
- let text, href;
- if (cap[2] === '@') {
- text = cap[0];
- href = 'mailto:' + text;
- }
- else {
- // do extended autolink path validation
- let prevCapZero;
- do {
- prevCapZero = cap[0];
- cap[0] = this.rules.inline._backpedal.exec(cap[0])?.[0] ?? '';
- } while (prevCapZero !== cap[0]);
- text = cap[0];
- if (cap[1] === 'www.') {
- href = 'http://' + cap[0];
- }
- else {
- href = cap[0];
- }
- }
- return {
- type: 'link',
- raw: cap[0],
- text,
- href,
- tokens: [
- {
- type: 'text',
- raw: text,
- text,
- },
- ],
- };
- }
- }
- inlineText(src) {
- const cap = this.rules.inline.text.exec(src);
- if (cap) {
- const escaped = this.lexer.state.inRawBlock;
- return {
- type: 'text',
- raw: cap[0],
- text: cap[0],
- escaped,
- };
- }
- }
-}
-
-/**
- * Block Lexer
- */
-class _Lexer {
- tokens;
- options;
- state;
- tokenizer;
- inlineQueue;
- constructor(options) {
- // TokenList cannot be created in one go
- this.tokens = [];
- this.tokens.links = Object.create(null);
- this.options = options || _defaults;
- this.options.tokenizer = this.options.tokenizer || new _Tokenizer();
- this.tokenizer = this.options.tokenizer;
- this.tokenizer.options = this.options;
- this.tokenizer.lexer = this;
- this.inlineQueue = [];
- this.state = {
- inLink: false,
- inRawBlock: false,
- top: true,
- };
- const rules = {
- other,
- block: block.normal,
- inline: inline.normal,
- };
- if (this.options.pedantic) {
- rules.block = block.pedantic;
- rules.inline = inline.pedantic;
- }
- else if (this.options.gfm) {
- rules.block = block.gfm;
- if (this.options.breaks) {
- rules.inline = inline.breaks;
- }
- else {
- rules.inline = inline.gfm;
- }
- }
- this.tokenizer.rules = rules;
- }
- /**
- * Expose Rules
- */
- static get rules() {
- return {
- block,
- inline,
- };
- }
- /**
- * Static Lex Method
- */
- static lex(src, options) {
- const lexer = new _Lexer(options);
- return lexer.lex(src);
- }
- /**
- * Static Lex Inline Method
- */
- static lexInline(src, options) {
- const lexer = new _Lexer(options);
- return lexer.inlineTokens(src);
- }
- /**
- * Preprocessing
- */
- lex(src) {
- src = src.replace(other.carriageReturn, '\n');
- this.blockTokens(src, this.tokens);
- for (let i = 0; i < this.inlineQueue.length; i++) {
- const next = this.inlineQueue[i];
- this.inlineTokens(next.src, next.tokens);
- }
- this.inlineQueue = [];
- return this.tokens;
- }
- blockTokens(src, tokens = [], lastParagraphClipped = false) {
- if (this.options.pedantic) {
- src = src.replace(other.tabCharGlobal, ' ').replace(other.spaceLine, '');
- }
- while (src) {
- let token;
- if (this.options.extensions?.block?.some((extTokenizer) => {
- if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- return true;
- }
- return false;
- })) {
- continue;
- }
- // newline
- if (token = this.tokenizer.space(src)) {
- src = src.substring(token.raw.length);
- const lastToken = tokens.at(-1);
- if (token.raw.length === 1 && lastToken !== undefined) {
- // if there's a single \n as a spacer, it's terminating the last line,
- // so move it there so that we don't get unnecessary paragraph tags
- lastToken.raw += '\n';
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // code
- if (token = this.tokenizer.code(src)) {
- src = src.substring(token.raw.length);
- const lastToken = tokens.at(-1);
- // An indented code block cannot interrupt a paragraph.
- if (lastToken?.type === 'paragraph' || lastToken?.type === 'text') {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.text;
- this.inlineQueue.at(-1).src = lastToken.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // fences
- if (token = this.tokenizer.fences(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // heading
- if (token = this.tokenizer.heading(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // hr
- if (token = this.tokenizer.hr(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // blockquote
- if (token = this.tokenizer.blockquote(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // list
- if (token = this.tokenizer.list(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // html
- if (token = this.tokenizer.html(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // def
- if (token = this.tokenizer.def(src)) {
- src = src.substring(token.raw.length);
- const lastToken = tokens.at(-1);
- if (lastToken?.type === 'paragraph' || lastToken?.type === 'text') {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.raw;
- this.inlineQueue.at(-1).src = lastToken.text;
- }
- else if (!this.tokens.links[token.tag]) {
- this.tokens.links[token.tag] = {
- href: token.href,
- title: token.title,
- };
- }
- continue;
- }
- // table (gfm)
- if (token = this.tokenizer.table(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // lheading
- if (token = this.tokenizer.lheading(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // top-level paragraph
- // prevent paragraph consuming extensions by clipping 'src' to extension start
- let cutSrc = src;
- if (this.options.extensions?.startBlock) {
- let startIndex = Infinity;
- const tempSrc = src.slice(1);
- let tempStart;
- this.options.extensions.startBlock.forEach((getStartIndex) => {
- tempStart = getStartIndex.call({ lexer: this }, tempSrc);
- if (typeof tempStart === 'number' && tempStart >= 0) {
- startIndex = Math.min(startIndex, tempStart);
- }
- });
- if (startIndex < Infinity && startIndex >= 0) {
- cutSrc = src.substring(0, startIndex + 1);
- }
- }
- if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
- const lastToken = tokens.at(-1);
- if (lastParagraphClipped && lastToken?.type === 'paragraph') {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.text;
- this.inlineQueue.pop();
- this.inlineQueue.at(-1).src = lastToken.text;
- }
- else {
- tokens.push(token);
- }
- lastParagraphClipped = cutSrc.length !== src.length;
- src = src.substring(token.raw.length);
- continue;
- }
- // text
- if (token = this.tokenizer.text(src)) {
- src = src.substring(token.raw.length);
- const lastToken = tokens.at(-1);
- if (lastToken?.type === 'text') {
- lastToken.raw += '\n' + token.raw;
- lastToken.text += '\n' + token.text;
- this.inlineQueue.pop();
- this.inlineQueue.at(-1).src = lastToken.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- if (src) {
- const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
- if (this.options.silent) {
- console.error(errMsg);
- break;
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- this.state.top = true;
- return tokens;
- }
- inline(src, tokens = []) {
- this.inlineQueue.push({ src, tokens });
- return tokens;
- }
- /**
- * Lexing/Compiling
- */
- inlineTokens(src, tokens = []) {
- // String with links masked to avoid interference with em and strong
- let maskedSrc = src;
- let match = null;
- // Mask out reflinks
- if (this.tokens.links) {
- const links = Object.keys(this.tokens.links);
- if (links.length > 0) {
- while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
- if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
- maskedSrc = maskedSrc.slice(0, match.index)
- + '[' + 'a'.repeat(match[0].length - 2) + ']'
- + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
- }
- }
- }
- }
- // Mask out other blocks
- while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
- maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
- }
- // Mask out escaped characters
- while ((match = this.tokenizer.rules.inline.anyPunctuation.exec(maskedSrc)) != null) {
- maskedSrc = maskedSrc.slice(0, match.index) + '++' + maskedSrc.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);
- }
- let keepPrevChar = false;
- let prevChar = '';
- while (src) {
- if (!keepPrevChar) {
- prevChar = '';
- }
- keepPrevChar = false;
- let token;
- // extensions
- if (this.options.extensions?.inline?.some((extTokenizer) => {
- if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- return true;
- }
- return false;
- })) {
- continue;
- }
- // escape
- if (token = this.tokenizer.escape(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // tag
- if (token = this.tokenizer.tag(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // link
- if (token = this.tokenizer.link(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // reflink, nolink
- if (token = this.tokenizer.reflink(src, this.tokens.links)) {
- src = src.substring(token.raw.length);
- const lastToken = tokens.at(-1);
- if (token.type === 'text' && lastToken?.type === 'text') {
- lastToken.raw += token.raw;
- lastToken.text += token.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- // em & strong
- if (token = this.tokenizer.emStrong(src, maskedSrc, prevChar)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // code
- if (token = this.tokenizer.codespan(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // br
- if (token = this.tokenizer.br(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // del (gfm)
- if (token = this.tokenizer.del(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // autolink
- if (token = this.tokenizer.autolink(src)) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // url (gfm)
- if (!this.state.inLink && (token = this.tokenizer.url(src))) {
- src = src.substring(token.raw.length);
- tokens.push(token);
- continue;
- }
- // text
- // prevent inlineText consuming extensions by clipping 'src' to extension start
- let cutSrc = src;
- if (this.options.extensions?.startInline) {
- let startIndex = Infinity;
- const tempSrc = src.slice(1);
- let tempStart;
- this.options.extensions.startInline.forEach((getStartIndex) => {
- tempStart = getStartIndex.call({ lexer: this }, tempSrc);
- if (typeof tempStart === 'number' && tempStart >= 0) {
- startIndex = Math.min(startIndex, tempStart);
- }
- });
- if (startIndex < Infinity && startIndex >= 0) {
- cutSrc = src.substring(0, startIndex + 1);
- }
- }
- if (token = this.tokenizer.inlineText(cutSrc)) {
- src = src.substring(token.raw.length);
- if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
- prevChar = token.raw.slice(-1);
- }
- keepPrevChar = true;
- const lastToken = tokens.at(-1);
- if (lastToken?.type === 'text') {
- lastToken.raw += token.raw;
- lastToken.text += token.text;
- }
- else {
- tokens.push(token);
- }
- continue;
- }
- if (src) {
- const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
- if (this.options.silent) {
- console.error(errMsg);
- break;
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- return tokens;
- }
-}
-
-/**
- * Renderer
- */
-class _Renderer {
- options;
- parser; // set by the parser
- constructor(options) {
- this.options = options || _defaults;
- }
- space(token) {
- return '';
- }
- code({ text, lang, escaped }) {
- const langString = (lang || '').match(other.notSpaceStart)?.[0];
- const code = text.replace(other.endingNewline, '') + '\n';
- if (!langString) {
- return ''
- + (escaped ? code : escape(code, true))
- + ' \n';
- }
- return ''
- + (escaped ? code : escape(code, true))
- + ' \n';
- }
- blockquote({ tokens }) {
- const body = this.parser.parse(tokens);
- return `\n${body} \n`;
- }
- html({ text }) {
- return text;
- }
- heading({ tokens, depth }) {
- return `${this.parser.parseInline(tokens)} \n`;
- }
- hr(token) {
- return ' \n';
- }
- list(token) {
- const ordered = token.ordered;
- const start = token.start;
- let body = '';
- for (let j = 0; j < token.items.length; j++) {
- const item = token.items[j];
- body += this.listitem(item);
- }
- const type = ordered ? 'ol' : 'ul';
- const startAttr = (ordered && start !== 1) ? (' start="' + start + '"') : '';
- return '<' + type + startAttr + '>\n' + body + '' + type + '>\n';
- }
- listitem(item) {
- let itemBody = '';
- if (item.task) {
- const checkbox = this.checkbox({ checked: !!item.checked });
- if (item.loose) {
- if (item.tokens[0]?.type === 'paragraph') {
- item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
- if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
- item.tokens[0].tokens[0].text = checkbox + ' ' + escape(item.tokens[0].tokens[0].text);
- item.tokens[0].tokens[0].escaped = true;
- }
- }
- else {
- item.tokens.unshift({
- type: 'text',
- raw: checkbox + ' ',
- text: checkbox + ' ',
- escaped: true,
- });
- }
- }
- else {
- itemBody += checkbox + ' ';
- }
- }
- itemBody += this.parser.parse(item.tokens, !!item.loose);
- return `${itemBody} \n`;
- }
- checkbox({ checked }) {
- return ' ';
- }
- paragraph({ tokens }) {
- return `${this.parser.parseInline(tokens)}
\n`;
- }
- table(token) {
- let header = '';
- // header
- let cell = '';
- for (let j = 0; j < token.header.length; j++) {
- cell += this.tablecell(token.header[j]);
- }
- header += this.tablerow({ text: cell });
- let body = '';
- for (let j = 0; j < token.rows.length; j++) {
- const row = token.rows[j];
- cell = '';
- for (let k = 0; k < row.length; k++) {
- cell += this.tablecell(row[k]);
- }
- body += this.tablerow({ text: cell });
- }
- if (body)
- body = `${body} `;
- return ' \n'
- + '\n'
- + header
- + ' \n'
- + body
- + '
\n';
- }
- tablerow({ text }) {
- return `\n${text} \n`;
- }
- tablecell(token) {
- const content = this.parser.parseInline(token.tokens);
- const type = token.header ? 'th' : 'td';
- const tag = token.align
- ? `<${type} align="${token.align}">`
- : `<${type}>`;
- return tag + content + `${type}>\n`;
- }
- /**
- * span level renderer
- */
- strong({ tokens }) {
- return `${this.parser.parseInline(tokens)} `;
- }
- em({ tokens }) {
- return `${this.parser.parseInline(tokens)} `;
- }
- codespan({ text }) {
- return `${escape(text, true)}`;
- }
- br(token) {
- return ' ';
- }
- del({ tokens }) {
- return `${this.parser.parseInline(tokens)}`;
- }
- link({ href, title, tokens }) {
- const text = this.parser.parseInline(tokens);
- const cleanHref = cleanUrl(href);
- if (cleanHref === null) {
- return text;
- }
- href = cleanHref;
- let out = '' + text + ' ';
- return out;
- }
- image({ href, title, text }) {
- const cleanHref = cleanUrl(href);
- if (cleanHref === null) {
- return escape(text);
- }
- href = cleanHref;
- let out = ` ';
- return out;
- }
- text(token) {
- return 'tokens' in token && token.tokens
- ? this.parser.parseInline(token.tokens)
- : ('escaped' in token && token.escaped ? token.text : escape(token.text));
- }
-}
-
-/**
- * TextRenderer
- * returns only the textual part of the token
- */
-class _TextRenderer {
- // no need for block level renderers
- strong({ text }) {
- return text;
- }
- em({ text }) {
- return text;
- }
- codespan({ text }) {
- return text;
- }
- del({ text }) {
- return text;
- }
- html({ text }) {
- return text;
- }
- text({ text }) {
- return text;
- }
- link({ text }) {
- return '' + text;
- }
- image({ text }) {
- return '' + text;
- }
- br() {
- return '';
- }
-}
-
-/**
- * Parsing & Compiling
- */
-class _Parser {
- options;
- renderer;
- textRenderer;
- constructor(options) {
- this.options = options || _defaults;
- this.options.renderer = this.options.renderer || new _Renderer();
- this.renderer = this.options.renderer;
- this.renderer.options = this.options;
- this.renderer.parser = this;
- this.textRenderer = new _TextRenderer();
- }
- /**
- * Static Parse Method
- */
- static parse(tokens, options) {
- const parser = new _Parser(options);
- return parser.parse(tokens);
- }
- /**
- * Static Parse Inline Method
- */
- static parseInline(tokens, options) {
- const parser = new _Parser(options);
- return parser.parseInline(tokens);
- }
- /**
- * Parse Loop
- */
- parse(tokens, top = true) {
- let out = '';
- for (let i = 0; i < tokens.length; i++) {
- const anyToken = tokens[i];
- // Run any renderer extensions
- if (this.options.extensions?.renderers?.[anyToken.type]) {
- const genericToken = anyToken;
- const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
- if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(genericToken.type)) {
- out += ret || '';
- continue;
- }
- }
- const token = anyToken;
- switch (token.type) {
- case 'space': {
- out += this.renderer.space(token);
- continue;
- }
- case 'hr': {
- out += this.renderer.hr(token);
- continue;
- }
- case 'heading': {
- out += this.renderer.heading(token);
- continue;
- }
- case 'code': {
- out += this.renderer.code(token);
- continue;
- }
- case 'table': {
- out += this.renderer.table(token);
- continue;
- }
- case 'blockquote': {
- out += this.renderer.blockquote(token);
- continue;
- }
- case 'list': {
- out += this.renderer.list(token);
- continue;
- }
- case 'html': {
- out += this.renderer.html(token);
- continue;
- }
- case 'paragraph': {
- out += this.renderer.paragraph(token);
- continue;
- }
- case 'text': {
- let textToken = token;
- let body = this.renderer.text(textToken);
- while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {
- textToken = tokens[++i];
- body += '\n' + this.renderer.text(textToken);
- }
- if (top) {
- out += this.renderer.paragraph({
- type: 'paragraph',
- raw: body,
- text: body,
- tokens: [{ type: 'text', raw: body, text: body, escaped: true }],
- });
- }
- else {
- out += body;
- }
- continue;
- }
- default: {
- const errMsg = 'Token with "' + token.type + '" type was not found.';
- if (this.options.silent) {
- console.error(errMsg);
- return '';
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- }
- return out;
- }
- /**
- * Parse Inline Tokens
- */
- parseInline(tokens, renderer = this.renderer) {
- let out = '';
- for (let i = 0; i < tokens.length; i++) {
- const anyToken = tokens[i];
- // Run any renderer extensions
- if (this.options.extensions?.renderers?.[anyToken.type]) {
- const ret = this.options.extensions.renderers[anyToken.type].call({ parser: this }, anyToken);
- if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(anyToken.type)) {
- out += ret || '';
- continue;
- }
- }
- const token = anyToken;
- switch (token.type) {
- case 'escape': {
- out += renderer.text(token);
- break;
- }
- case 'html': {
- out += renderer.html(token);
- break;
- }
- case 'link': {
- out += renderer.link(token);
- break;
- }
- case 'image': {
- out += renderer.image(token);
- break;
- }
- case 'strong': {
- out += renderer.strong(token);
- break;
- }
- case 'em': {
- out += renderer.em(token);
- break;
- }
- case 'codespan': {
- out += renderer.codespan(token);
- break;
- }
- case 'br': {
- out += renderer.br(token);
- break;
- }
- case 'del': {
- out += renderer.del(token);
- break;
- }
- case 'text': {
- out += renderer.text(token);
- break;
- }
- default: {
- const errMsg = 'Token with "' + token.type + '" type was not found.';
- if (this.options.silent) {
- console.error(errMsg);
- return '';
- }
- else {
- throw new Error(errMsg);
- }
- }
- }
- }
- return out;
- }
-}
-
-class _Hooks {
- options;
- block;
- constructor(options) {
- this.options = options || _defaults;
- }
- static passThroughHooks = new Set([
- 'preprocess',
- 'postprocess',
- 'processAllTokens',
- ]);
- /**
- * Process markdown before marked
- */
- preprocess(markdown) {
- return markdown;
- }
- /**
- * Process HTML after marked is finished
- */
- postprocess(html) {
- return html;
- }
- /**
- * Process all tokens before walk tokens
- */
- processAllTokens(tokens) {
- return tokens;
- }
- /**
- * Provide function to tokenize markdown
- */
- provideLexer() {
- return this.block ? _Lexer.lex : _Lexer.lexInline;
- }
- /**
- * Provide function to parse tokens
- */
- provideParser() {
- return this.block ? _Parser.parse : _Parser.parseInline;
- }
-}
-
-class Marked {
- defaults = _getDefaults();
- options = this.setOptions;
- parse = this.parseMarkdown(true);
- parseInline = this.parseMarkdown(false);
- Parser = _Parser;
- Renderer = _Renderer;
- TextRenderer = _TextRenderer;
- Lexer = _Lexer;
- Tokenizer = _Tokenizer;
- Hooks = _Hooks;
- constructor(...args) {
- this.use(...args);
- }
- /**
- * Run callback for every token
- */
- walkTokens(tokens, callback) {
- let values = [];
- for (const token of tokens) {
- values = values.concat(callback.call(this, token));
- switch (token.type) {
- case 'table': {
- const tableToken = token;
- for (const cell of tableToken.header) {
- values = values.concat(this.walkTokens(cell.tokens, callback));
- }
- for (const row of tableToken.rows) {
- for (const cell of row) {
- values = values.concat(this.walkTokens(cell.tokens, callback));
- }
- }
- break;
- }
- case 'list': {
- const listToken = token;
- values = values.concat(this.walkTokens(listToken.items, callback));
- break;
- }
- default: {
- const genericToken = token;
- if (this.defaults.extensions?.childTokens?.[genericToken.type]) {
- this.defaults.extensions.childTokens[genericToken.type].forEach((childTokens) => {
- const tokens = genericToken[childTokens].flat(Infinity);
- values = values.concat(this.walkTokens(tokens, callback));
- });
- }
- else if (genericToken.tokens) {
- values = values.concat(this.walkTokens(genericToken.tokens, callback));
- }
- }
- }
- }
- return values;
- }
- use(...args) {
- const extensions = this.defaults.extensions || { renderers: {}, childTokens: {} };
- args.forEach((pack) => {
- // copy options to new object
- const opts = { ...pack };
- // set async to true if it was set to true before
- opts.async = this.defaults.async || opts.async || false;
- // ==-- Parse "addon" extensions --== //
- if (pack.extensions) {
- pack.extensions.forEach((ext) => {
- if (!ext.name) {
- throw new Error('extension name required');
- }
- if ('renderer' in ext) { // Renderer extensions
- const prevRenderer = extensions.renderers[ext.name];
- if (prevRenderer) {
- // Replace extension with func to run new extension but fall back if false
- extensions.renderers[ext.name] = function (...args) {
- let ret = ext.renderer.apply(this, args);
- if (ret === false) {
- ret = prevRenderer.apply(this, args);
- }
- return ret;
- };
- }
- else {
- extensions.renderers[ext.name] = ext.renderer;
- }
- }
- if ('tokenizer' in ext) { // Tokenizer Extensions
- if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
- throw new Error("extension level must be 'block' or 'inline'");
- }
- const extLevel = extensions[ext.level];
- if (extLevel) {
- extLevel.unshift(ext.tokenizer);
- }
- else {
- extensions[ext.level] = [ext.tokenizer];
- }
- if (ext.start) { // Function to check for start of token
- if (ext.level === 'block') {
- if (extensions.startBlock) {
- extensions.startBlock.push(ext.start);
- }
- else {
- extensions.startBlock = [ext.start];
- }
- }
- else if (ext.level === 'inline') {
- if (extensions.startInline) {
- extensions.startInline.push(ext.start);
- }
- else {
- extensions.startInline = [ext.start];
- }
- }
- }
- }
- if ('childTokens' in ext && ext.childTokens) { // Child tokens to be visited by walkTokens
- extensions.childTokens[ext.name] = ext.childTokens;
- }
- });
- opts.extensions = extensions;
- }
- // ==-- Parse "overwrite" extensions --== //
- if (pack.renderer) {
- const renderer = this.defaults.renderer || new _Renderer(this.defaults);
- for (const prop in pack.renderer) {
- if (!(prop in renderer)) {
- throw new Error(`renderer '${prop}' does not exist`);
- }
- if (['options', 'parser'].includes(prop)) {
- // ignore options property
- continue;
- }
- const rendererProp = prop;
- const rendererFunc = pack.renderer[rendererProp];
- const prevRenderer = renderer[rendererProp];
- // Replace renderer with func to run extension, but fall back if false
- renderer[rendererProp] = (...args) => {
- let ret = rendererFunc.apply(renderer, args);
- if (ret === false) {
- ret = prevRenderer.apply(renderer, args);
- }
- return ret || '';
- };
- }
- opts.renderer = renderer;
- }
- if (pack.tokenizer) {
- const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
- for (const prop in pack.tokenizer) {
- if (!(prop in tokenizer)) {
- throw new Error(`tokenizer '${prop}' does not exist`);
- }
- if (['options', 'rules', 'lexer'].includes(prop)) {
- // ignore options, rules, and lexer properties
- continue;
- }
- const tokenizerProp = prop;
- const tokenizerFunc = pack.tokenizer[tokenizerProp];
- const prevTokenizer = tokenizer[tokenizerProp];
- // Replace tokenizer with func to run extension, but fall back if false
- // @ts-expect-error cannot type tokenizer function dynamically
- tokenizer[tokenizerProp] = (...args) => {
- let ret = tokenizerFunc.apply(tokenizer, args);
- if (ret === false) {
- ret = prevTokenizer.apply(tokenizer, args);
- }
- return ret;
- };
- }
- opts.tokenizer = tokenizer;
- }
- // ==-- Parse Hooks extensions --== //
- if (pack.hooks) {
- const hooks = this.defaults.hooks || new _Hooks();
- for (const prop in pack.hooks) {
- if (!(prop in hooks)) {
- throw new Error(`hook '${prop}' does not exist`);
- }
- if (['options', 'block'].includes(prop)) {
- // ignore options and block properties
- continue;
- }
- const hooksProp = prop;
- const hooksFunc = pack.hooks[hooksProp];
- const prevHook = hooks[hooksProp];
- if (_Hooks.passThroughHooks.has(prop)) {
- // @ts-expect-error cannot type hook function dynamically
- hooks[hooksProp] = (arg) => {
- if (this.defaults.async) {
- return Promise.resolve(hooksFunc.call(hooks, arg)).then(ret => {
- return prevHook.call(hooks, ret);
- });
- }
- const ret = hooksFunc.call(hooks, arg);
- return prevHook.call(hooks, ret);
- };
- }
- else {
- // @ts-expect-error cannot type hook function dynamically
- hooks[hooksProp] = (...args) => {
- let ret = hooksFunc.apply(hooks, args);
- if (ret === false) {
- ret = prevHook.apply(hooks, args);
- }
- return ret;
- };
- }
- }
- opts.hooks = hooks;
- }
- // ==-- Parse WalkTokens extensions --== //
- if (pack.walkTokens) {
- const walkTokens = this.defaults.walkTokens;
- const packWalktokens = pack.walkTokens;
- opts.walkTokens = function (token) {
- let values = [];
- values.push(packWalktokens.call(this, token));
- if (walkTokens) {
- values = values.concat(walkTokens.call(this, token));
- }
- return values;
- };
- }
- this.defaults = { ...this.defaults, ...opts };
- });
- return this;
- }
- setOptions(opt) {
- this.defaults = { ...this.defaults, ...opt };
- return this;
- }
- lexer(src, options) {
- return _Lexer.lex(src, options ?? this.defaults);
- }
- parser(tokens, options) {
- return _Parser.parse(tokens, options ?? this.defaults);
- }
- parseMarkdown(blockType) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const parse = (src, options) => {
- const origOpt = { ...options };
- const opt = { ...this.defaults, ...origOpt };
- const throwError = this.onError(!!opt.silent, !!opt.async);
- // throw error if an extension set async to true but parse was called with async: false
- if (this.defaults.async === true && origOpt.async === false) {
- return throwError(new Error('marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise.'));
- }
- // throw error in case of non string input
- if (typeof src === 'undefined' || src === null) {
- return throwError(new Error('marked(): input parameter is undefined or null'));
- }
- if (typeof src !== 'string') {
- return throwError(new Error('marked(): input parameter is of type '
- + Object.prototype.toString.call(src) + ', string expected'));
- }
- if (opt.hooks) {
- opt.hooks.options = opt;
- opt.hooks.block = blockType;
- }
- const lexer = opt.hooks ? opt.hooks.provideLexer() : (blockType ? _Lexer.lex : _Lexer.lexInline);
- const parser = opt.hooks ? opt.hooks.provideParser() : (blockType ? _Parser.parse : _Parser.parseInline);
- if (opt.async) {
- return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
- .then(src => lexer(src, opt))
- .then(tokens => opt.hooks ? opt.hooks.processAllTokens(tokens) : tokens)
- .then(tokens => opt.walkTokens ? Promise.all(this.walkTokens(tokens, opt.walkTokens)).then(() => tokens) : tokens)
- .then(tokens => parser(tokens, opt))
- .then(html => opt.hooks ? opt.hooks.postprocess(html) : html)
- .catch(throwError);
- }
- try {
- if (opt.hooks) {
- src = opt.hooks.preprocess(src);
- }
- let tokens = lexer(src, opt);
- if (opt.hooks) {
- tokens = opt.hooks.processAllTokens(tokens);
- }
- if (opt.walkTokens) {
- this.walkTokens(tokens, opt.walkTokens);
- }
- let html = parser(tokens, opt);
- if (opt.hooks) {
- html = opt.hooks.postprocess(html);
- }
- return html;
- }
- catch (e) {
- return throwError(e);
- }
- };
- return parse;
- }
- onError(silent, async) {
- return (e) => {
- e.message += '\nPlease report this to https://github.com/markedjs/marked.';
- if (silent) {
- const msg = 'An error occurred:
'
- + escape(e.message + '', true)
- + ' ';
- if (async) {
- return Promise.resolve(msg);
- }
- return msg;
- }
- if (async) {
- return Promise.reject(e);
- }
- throw e;
- };
- }
-}
-
-const markedInstance = new Marked();
-function marked(src, opt) {
- return markedInstance.parse(src, opt);
-}
-/**
- * Sets the default options.
- *
- * @param options Hash of options
- */
-marked.options =
- marked.setOptions = function (options) {
- markedInstance.setOptions(options);
- marked.defaults = markedInstance.defaults;
- changeDefaults(marked.defaults);
- return marked;
- };
-/**
- * Gets the original marked default options.
- */
-marked.getDefaults = _getDefaults;
-marked.defaults = _defaults;
-/**
- * Use Extension
- */
-marked.use = function (...args) {
- markedInstance.use(...args);
- marked.defaults = markedInstance.defaults;
- changeDefaults(marked.defaults);
- return marked;
-};
-/**
- * Run callback for every token
- */
-marked.walkTokens = function (tokens, callback) {
- return markedInstance.walkTokens(tokens, callback);
-};
-/**
- * Compiles markdown to HTML without enclosing `p` tag.
- *
- * @param src String of markdown source to be compiled
- * @param options Hash of options
- * @return String of compiled HTML
- */
-marked.parseInline = markedInstance.parseInline;
-/**
- * Expose
- */
-marked.Parser = _Parser;
-marked.parser = _Parser.parse;
-marked.Renderer = _Renderer;
-marked.TextRenderer = _TextRenderer;
-marked.Lexer = _Lexer;
-marked.lexer = _Lexer.lex;
-marked.Tokenizer = _Tokenizer;
-marked.Hooks = _Hooks;
-marked.parse = marked;
-const options = marked.options;
-const setOptions = marked.setOptions;
-const use = marked.use;
-const walkTokens = marked.walkTokens;
-const parseInline = marked.parseInline;
-const parse = marked;
-const parser = _Parser.parse;
-const lexer = _Lexer.lex;
-
-export { _Hooks as Hooks, _Lexer as Lexer, Marked, _Parser as Parser, _Renderer as Renderer, _TextRenderer as TextRenderer, _Tokenizer as Tokenizer, _defaults as defaults, _getDefaults as getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };
-//# sourceMappingURL=marked.esm.js.map
diff --git a/js/marked-config.js b/js/marked-config.js
deleted file mode 100644
index 46f500d..0000000
--- a/js/marked-config.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Markdown 渲染器配置
- * 封装 marked.js,配置自定义渲染器和安全策略
- */
-
-import { marked } from './lib/marked.esm.js';
-import { SAFE_URI_SCHEMES } from './sanitizer.js';
-
-// 配置 marked
-marked.setOptions({
- breaks: true, // 换行转
- gfm: true, // GitHub 风格 Markdown
-});
-
-// 自定义渲染器:链接新窗口打开 + 安全属性
-const renderer = new marked.Renderer();
-
-renderer.link = function ({ href, title, text }) {
- const safe = SAFE_URI_SCHEMES.has(new URL(href, 'https://placeholder').protocol);
- const t = title ? ` title="${title}"` : '';
- if (!safe) {
- return `${text} `;
- }
- return `${text} `;
-};
-
-renderer.image = function ({ href, title, text }) {
- const safe = SAFE_URI_SCHEMES.has(new URL(href, 'https://placeholder').protocol);
- if (!safe && !/^data:image\//i.test(href)) {
- return `[已阻止不安全图片: ${text}] `;
- }
- const t = title ? ` title="${title}"` : '';
- return ` `;
-};
-
-marked.use({ renderer });
-
-export { marked };
diff --git a/js/ollama-api.js b/js/ollama-api.js
deleted file mode 100644
index c08cc3f..0000000
--- a/js/ollama-api.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * Ollama API 客户端封装
- *
- * 基于 Fetch API 与 Ollama REST 接口交互
- * 支持:流式聊天、模型管理、连接检测
- *
- * API 文档:https://docs.ollama.com/api
- */
-
-export class OllamaAPI {
- constructor(baseUrl = 'http://127.0.0.1:11434') {
- this.baseUrl = baseUrl.replace(/\/+$/, '');
- }
-
- /** 通用请求方法 */
- async _request(path, options = {}) {
- const url = `${this.baseUrl}${path}`;
- const config = {
- ...options,
- headers: { 'Content-Type': 'application/json', ...options.headers }
- };
- const response = await fetch(url, config);
- if (!response.ok) {
- const errorBody = await response.text().catch(() => '');
- throw new Error(`Ollama API 错误 ${response.status}: ${errorBody || response.statusText}`);
- }
- return response;
- }
-
- /** JSON 请求快捷方法 */
- async _json(path, body = null) {
- const options = body ? { method: 'POST', body: JSON.stringify(body) } : {};
- const response = await this._request(path, options);
- return response.json();
- }
-
- // ── 模型管理 ──
-
- /** 获取已安装模型列表 */
- async listModels() { return this._json('/api/tags'); }
-
- /** 获取运行中的模型列表 */
- async psModels() { return this._json('/api/ps'); }
-
- /** 获取 Ollama 版本 */
- async getVersion() { return this._json('/api/version'); }
-
- /** 查看模型详情 */
- async showModel(model) { return this._json('/api/show', { model }); }
-
- // ── 聊天 API ──
-
- /** 非流式聊天 */
- async chat(params) {
- const body = {
- model: params.model,
- messages: params.messages,
- stream: false,
- ...(params.think !== undefined && { think: params.think }),
- ...(params.system && { system: params.system }),
- ...(params.keep_alive !== undefined && { keep_alive: params.keep_alive }),
- ...(params.options && { options: params.options })
- };
- return this._json('/api/chat', body);
- }
-
- /**
- * 流式聊天
- * 使用 ReadableStream + TextDecoder 解析 NDJSON 流
- * Ollama 流式响应格式 (NDJSON):
- * {"model":"xxx","message":{"role":"assistant","content":"Hello"},"done":false}
- *
- * @param {AbortController} [abortController] - 可选,用于强制终止流
- */
- async chatStream(params, onChunk, abortController) {
- const body = {
- model: params.model,
- messages: params.messages,
- stream: true,
- ...(params.think !== undefined && { think: params.think }),
- ...(params.system && { system: params.system }),
- ...(params.keep_alive !== undefined && { keep_alive: params.keep_alive }),
- ...(params.options && { options: params.options })
- };
-
- const fetchOptions = {
- method: 'POST',
- body: JSON.stringify(body)
- };
-
- // 注入 abort 信号,同时中止 fetch 和 reader
- if (abortController) {
- fetchOptions.signal = abortController.signal;
- }
-
- const response = await this._request('/api/chat', fetchOptions);
-
- const reader = response.body.getReader();
- const decoder = new TextDecoder();
- let buffer = '';
-
- // 如果外部触发 abort,立即取消 reader(双重保险:fetch abort + reader.cancel)
- if (abortController) {
- abortController.signal.addEventListener('abort', () => {
- reader.cancel();
- }, { once: true });
- }
-
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
-
- buffer += decoder.decode(value, { stream: true });
- const lines = buffer.split('\n');
- buffer = lines.pop() || '';
-
- for (const line of lines) {
- if (!line.trim()) continue;
- try {
- const chunk = JSON.parse(line);
- if (onChunk) onChunk(chunk);
- if (chunk.done) {
- reader.cancel();
- return;
- }
- } catch (err) {
- console.warn('[OllamaAPI] 流式数据解析警告:', err.message);
- }
- }
- }
-
- if (buffer.trim()) {
- try {
- const chunk = JSON.parse(buffer);
- if (onChunk) onChunk(chunk);
- } catch { /* 忽略 */ }
- }
- }
-
- /** 流式生成(单轮) */
- async generateStream(params, onChunk) {
- const body = {
- model: params.model,
- prompt: params.prompt,
- stream: true,
- ...(params.images && { images: params.images }),
- ...(params.options && { options: params.options })
- };
-
- const response = await this._request('/api/generate', {
- method: 'POST',
- body: JSON.stringify(body)
- });
-
- const reader = response.body.getReader();
- const decoder = new TextDecoder();
- let buffer = '';
-
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
-
- buffer += decoder.decode(value, { stream: true });
- const lines = buffer.split('\n');
- buffer = lines.pop() || '';
-
- for (const line of lines) {
- if (!line.trim()) continue;
- try {
- const chunk = JSON.parse(line);
- if (onChunk) onChunk(chunk);
- if (chunk.done) {
- reader.cancel();
- return;
- }
- } catch { /* 忽略不完整数据 */ }
- }
- }
- }
-
- /** 生成嵌入向量 */
- async embed(model, input) {
- return this._json('/api/embed', { model, input });
- }
-}
diff --git a/js/preset-manager.js b/js/preset-manager.js
deleted file mode 100644
index 7b63762..0000000
--- a/js/preset-manager.js
+++ /dev/null
@@ -1,319 +0,0 @@
-/**
- * PresetManager - Agent 预设管理
- *
- * 预设数据结构:
- * {
- * id: string,
- * name: string, // 预设名称,如 "翻译官"
- * icon: string, // emoji 图标
- * systemPrompt: string, // 系统提示词
- * temperature: number, // 温度 (0~2)
- * numCtx: number, // 上下文长度
- * think: boolean, // Think 开关
- * createdAt: number,
- * updatedAt: number,
- * }
- */
-
-import { state, KEYS } from './state.js';
-import { generateId } from './utils.js';
-
-const PRESETS_KEY = 'agentPresets';
-const ACTIVE_PRESET_KEY = 'activePresetId';
-
-// ── 内置预设 ──
-const BUILT_IN_PRESETS = [
- {
- id: '_default',
- name: '默认',
- icon: '💬',
- systemPrompt: '',
- temperature: 0.7,
- numCtx: 24576,
- think: false,
- builtIn: true,
- createdAt: 0,
- updatedAt: 0
- },
- {
- id: '_translator',
- name: '翻译官',
- icon: '🌐',
- systemPrompt: '你是一位专业翻译官。你的任务是将用户输入的内容准确翻译为目标语言。\n\n规则:\n1. 自动识别源语言,翻译为用户指定的目标语言(如未指定则翻译为中文)\n2. 保持原文的格式、语气和风格\n3. 专有名词保留原文并给出通用译名\n4. 对于有多种含义的词语,结合上下文选择最合适的翻译\n5. 直接输出翻译结果,不需要额外解释(除非用户要求)',
- temperature: 0.3,
- numCtx: 16384,
- think: false,
- builtIn: true,
- createdAt: 0,
- updatedAt: 0
- },
- {
- id: '_code_review',
- name: '代码审查',
- icon: '🔍',
- systemPrompt: '你是一位资深代码审查专家。对用户提交的代码进行全面审查。\n\n审查维度:\n1. **Bug 与逻辑错误** — 潜在的运行时错误、边界条件、空指针等\n2. **安全性** — SQL注入、XSS、敏感信息泄露等安全隐患\n3. **性能** — 时间/空间复杂度、不必要的计算、内存泄漏\n4. **可读性** — 命名规范、代码结构、注释质量\n5. **最佳实践** — 设计模式、语言惯用法、框架约定\n\n输出格式:\n- 按严重程度排列问题(严重 → 轻微)\n- 每个问题给出:位置、问题描述、修复建议、改进后的代码\n- 最后给出整体评价和改进建议',
- temperature: 0.2,
- numCtx: 32768,
- think: true,
- builtIn: true,
- createdAt: 0,
- updatedAt: 0
- },
- {
- id: '_writer',
- name: '写作助手',
- icon: '✍️',
- systemPrompt: '你是一位才华横溢的写作助手。擅长多种文体和写作风格。\n\n能力范围:\n1. 文章撰写 — 议论文、说明文、散文、故事\n2. 文案创作 — 广告文案、产品描述、社交媒体文案\n3. 内容润色 — 修改语法、优化措辞、提升表达力\n4. 风格调整 — 正式/口语/文艺/简洁等多种风格\n5. 创意写作 — 小说、诗歌、剧本、对话\n\n工作方式:\n- 仔细理解用户的写作需求(读者、用途、风格偏好)\n- 如需信息不全,主动询问确认\n- 提供多种版本供选择\n- 解释重要的写作决策',
- temperature: 0.9,
- numCtx: 24576,
- think: false,
- builtIn: true,
- createdAt: 0,
- updatedAt: 0
- },
- {
- id: '_analyst',
- name: '数据分析师',
- icon: '📊',
- systemPrompt: '你是一位专业的数据分析师。擅长从数据中提取洞察并给出可执行的建议。\n\n分析方法:\n1. 数据概览 — 数据量、字段类型、缺失值、异常值\n2. 统计分析 — 描述性统计、相关性分析、趋势分析\n3. 可视化建议 — 推荐合适的图表类型和展示方式\n4. 洞察提取 — 关键发现、模式识别、因果推断\n5. 行动建议 — 基于数据的具体可执行建议\n\n输出格式:\n- 先给出核心结论(Executive Summary)\n- 再展开详细分析过程\n- 使用表格和列表组织数据\n- 给出代码(Python/SQL)时附带注释',
- temperature: 0.4,
- numCtx: 32768,
- think: true,
- builtIn: true,
- createdAt: 0,
- updatedAt: 0
- },
- {
- id: '_tutor',
- name: '学习导师',
- icon: '🎓',
- systemPrompt: '你是一位耐心且善于启发的学习导师。用苏格拉底式教学法帮助用户深入理解知识。\n\n教学原则:\n1. **先问后答** — 通过引导性问题帮助用户自己找到答案\n2. **由浅入深** — 从基础概念开始,逐步构建知识体系\n3. **类比教学** — 用生活中的类比解释抽象概念\n4. **实践导向** — 每个知识点配一个动手练习\n5. **鼓励探索** — 提供延伸阅读方向和思考题\n\n当用户提问时:\n- 先评估用户当前理解水平\n- 用引导性问题帮助思考\n- 如果用户确实卡住,再给出答案并解释思路\n- 每个概念学完后给出一个小练习',
- temperature: 0.6,
- numCtx: 24576,
- think: true,
- builtIn: true,
- createdAt: 0,
- updatedAt: 0
- }
-];
-
-let presets = [];
-let activePresetId = null;
-
-/**
- * 初始化预设管理器
- */
-export async function initPresetManager() {
- const db = state.get(KEYS.DB);
-
- // 从 DB 加载用户自定义预设
- let userPresets = [];
- if (db) {
- userPresets = await db.getSetting(PRESETS_KEY, []);
- }
-
- // 合并:内置 + 用户自定义
- presets = [...BUILT_IN_PRESETS, ...userPresets];
-
- // 加载上次激活的预设
- if (db) {
- activePresetId = await db.getSetting(ACTIVE_PRESET_KEY, '_default');
- } else {
- activePresetId = '_default';
- }
-
- // 写入 state
- state.set('presets', presets);
- state.set('activePresetId', activePresetId);
-
- // 应用当前预设(恢复设置)
- const activePreset = presets.find(p => p.id === activePresetId);
- if (activePreset && activePreset.id !== '_default') {
- applyPresetToState(activePreset, false); // 不再 DB,已在 init 阶段加载
- }
-}
-
-/**
- * 获取所有预设
- */
-export function getPresets() {
- return presets;
-}
-
-/**
- * 获取当前激活预设
- */
-export function getActivePreset() {
- return presets.find(p => p.id === activePresetId) || presets[0];
-}
-
-/**
- * 获取当前激活预设 ID
- */
-export function getActivePresetId() {
- return activePresetId;
-}
-
-/**
- * 激活预设
- */
-export async function activatePreset(presetId) {
- const preset = presets.find(p => p.id === presetId);
- if (!preset) return;
-
- activePresetId = presetId;
- state.set('activePresetId', presetId);
-
- applyPresetToState(preset, true);
-
- const db = state.get(KEYS.DB);
- if (db) {
- await db.saveSetting(ACTIVE_PRESET_KEY, presetId);
- }
-}
-
-/**
- * 将预设配置应用到全局 state
- */
-function applyPresetToState(preset, saveToDb) {
- const db = state.get(KEYS.DB);
-
- // 系统提示词
- if (preset.systemPrompt) {
- state.set(KEYS.SYSTEM_PROMPT_ENABLED, true);
- state.set(KEYS.SYSTEM_PROMPT, preset.systemPrompt);
- } else {
- state.set(KEYS.SYSTEM_PROMPT_ENABLED, false);
- state.set(KEYS.SYSTEM_PROMPT, '');
- }
-
- // 上下文长度
- if (preset.numCtx) {
- state.set(KEYS.NUM_CTX, preset.numCtx);
- }
-
- // 温度(存储到 state,聊天时使用)
- state.set('temperature', preset.temperature ?? 0.7);
-
- // Think 开关
- state.set('thinkEnabled', preset.think ?? false);
-
- // 同步到 UI
- syncPresetToUI(preset);
-
- // 持久化
- if (saveToDb && db) {
- db.saveSetting('systemPromptEnabled', preset.systemPrompt ? true : false);
- db.saveSetting('systemPrompt', preset.systemPrompt || '');
- db.saveSetting('numCtx', preset.numCtx || 24576);
- db.saveSetting('temperature', preset.temperature ?? 0.7);
- }
-}
-
-/**
- * 将预设配置同步到设置面板 UI
- */
-function syncPresetToUI(preset) {
- // 系统提示词
- const toggleSP = document.querySelector('#toggleSystemPrompt');
- const inputSP = document.querySelector('#inputSystemPrompt');
- if (toggleSP) toggleSP.checked = !!preset.systemPrompt;
- if (inputSP) {
- inputSP.disabled = !preset.systemPrompt;
- inputSP.value = preset.systemPrompt || '';
- }
-
- // numCtx
- const inputCtx = document.querySelector('#inputNumCtx');
- if (inputCtx) inputCtx.value = preset.numCtx || 24576;
-
- // Temperature
- const tempSlider = document.querySelector('#inputTemperature');
- const tempDisplay = document.querySelector('#tempValue');
- if (tempSlider) tempSlider.value = preset.temperature ?? 0.7;
- if (tempDisplay) tempDisplay.textContent = (preset.temperature ?? 0.7).toFixed(1);
-
- // Think toggle — 仅在模型支持时才设为 checked,否则保持 unchecked
- const thinkCheckbox = document.querySelector('#toggleThink');
- if (thinkCheckbox) {
- const modelSupportsThink = !thinkCheckbox.disabled;
- thinkCheckbox.checked = modelSupportsThink && (preset.think ?? false);
- }
-}
-
-/**
- * 创建预设
- */
-export async function createPreset({ name, icon, systemPrompt, temperature, numCtx, think }) {
- const preset = {
- id: `preset_${generateId()}`,
- name: name || '新预设',
- icon: icon || '🤖',
- systemPrompt: systemPrompt || '',
- temperature: temperature ?? 0.7,
- numCtx: numCtx ?? 24576,
- think: think ?? false,
- createdAt: Date.now(),
- updatedAt: Date.now()
- };
-
- presets.push(preset);
- state.set('presets', [...presets]);
-
- await saveUserPresets();
- return preset;
-}
-
-/**
- * 更新预设
- */
-export async function updatePreset(presetId, updates) {
- const idx = presets.findIndex(p => p.id === presetId);
- if (idx === -1) return;
-
- // 内置预设不允许修改
- if (presets[idx].builtIn) return;
-
- presets[idx] = { ...presets[idx], ...updates, updatedAt: Date.now() };
- state.set('presets', [...presets]);
-
- // 如果更新的是当前激活预设,重新应用
- if (activePresetId === presetId) {
- applyPresetToState(presets[idx], true);
- }
-
- await saveUserPresets();
-}
-
-/**
- * 删除预设
- */
-export async function deletePreset(presetId) {
- const idx = presets.findIndex(p => p.id === presetId);
- if (idx === -1) return;
- if (presets[idx].builtIn) return; // 内置不可删
-
- presets.splice(idx, 1);
- state.set('presets', [...presets]);
-
- // 如果删除的是当前激活预设,切回默认
- if (activePresetId === presetId) {
- await activatePreset('_default');
- }
-
- await saveUserPresets();
-}
-
-/**
- * 保存用户自定义预设到 IndexedDB
- */
-async function saveUserPresets() {
- const db = state.get(KEYS.DB);
- if (!db) return;
-
- const userPresets = presets
- .filter(p => !p.builtIn)
- .map(({ builtIn, ...rest }) => rest); // 去掉 builtIn 标记
-
- await db.saveSetting(PRESETS_KEY, userPresets);
-}
diff --git a/js/rag.js b/js/rag.js
deleted file mode 100644
index 904c04a..0000000
--- a/js/rag.js
+++ /dev/null
@@ -1,202 +0,0 @@
-/**
- * RAG - 检索增强生成管线
- *
- * 查询 → 嵌入 → 向量检索 → 构造上下文 → 注入 prompt
- */
-
-import { state, KEYS } from './state.js';
-import { VectorStore } from './vector-store.js';
-import { chunkText, createChunkMetadata } from './document-processor.js';
-import { showToast } from './components/toast.js';
-
-let vectorStore = null;
-
-/**
- * 初始化向量存储
- */
-export async function initVectorStore() {
- if (vectorStore) return vectorStore;
- vectorStore = new VectorStore();
- await vectorStore.init();
- return vectorStore;
-}
-
-/**
- * 获取向量存储实例
- */
-export function getVectorStore() {
- return vectorStore;
-}
-
-/**
- * 使用 Ollama 生成文本嵌入向量
- * @param {string} text
- * @param {string} model - 嵌入模型名称
- * @returns {number[]}
- */
-export async function embedText(text, model) {
- const api = state.get(KEYS.API);
- if (!api) throw new Error('Ollama API 未连接');
-
- const result = await api.embed(model, text);
- // /api/embed 返回 { embedding: [...] } 或 { embeddings: [[...]] }
- if (result.embedding) return result.embedding;
- if (result.embeddings && result.embeddings[0]) return result.embeddings[0];
- throw new Error('嵌入向量返回格式异常');
-}
-
-/**
- * 批量嵌入(分批处理,避免超时)
- * @param {string[]} texts
- * @param {string} model
- * @param {function} onProgress - (done, total) => void
- * @returns {number[][]}
- */
-export async function embedBatch(texts, model, onProgress) {
- const results = [];
- const batchSize = 5;
- for (let i = 0; i < texts.length; i += batchSize) {
- const batch = texts.slice(i, i + batchSize);
- const embeddings = await Promise.all(batch.map(t => embedText(t, model)));
- results.push(...embeddings);
- if (onProgress) onProgress(Math.min(i + batchSize, texts.length), texts.length);
- }
- return results;
-}
-
-/**
- * 向知识库集合添加文档
- * @param {string} colId - 集合 ID
- * @param {string} filename - 文件名
- * @param {string} content - 文件内容
- * @param {string} embedModel - 嵌入模型
- * @param {function} onProgress
- */
-export async function addDocumentToKB(colId, filename, content, embedModel, onProgress) {
- const vs = await initVectorStore();
-
- // 1. 分块
- const textChunks = chunkText(content);
- if (textChunks.length === 0) throw new Error('文档内容为空,无法处理');
-
- // 2. 生成文档 ID
- const docId = `doc_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
-
- // 3. 生成嵌入向量
- if (onProgress) onProgress(0, textChunks.length, '正在生成向量...');
- const embeddings = await embedBatch(textChunks, embedModel, (done, total) => {
- if (onProgress) onProgress(done, total, '正在生成向量...');
- });
-
- // 4. 构造向量数据
- const vectors = textChunks.map((chunk, i) => {
- const meta = createChunkMetadata(chunk, i, docId, filename);
- return {
- ...meta,
- collectionId: colId,
- embedding: embeddings[i]
- };
- });
-
- // 5. 存储
- if (onProgress) onProgress(textChunks.length, textChunks.length, '正在保存...');
- await vs.addVectors(vectors);
-
- // 6. 更新集合统计
- const col = await vs.getCollection(colId);
- if (col) {
- col.docCount = (col.docCount || 0) + 1;
- col.chunkCount = (col.chunkCount || 0) + textChunks.length;
- col.embeddingModel = embedModel;
- await vs.updateCollection(col);
- }
-
- return { docId, chunkCount: textChunks.length };
-}
-
-/**
- * 从知识库删除文档
- */
-export async function removeDocumentFromKB(colId, docId) {
- const vs = await initVectorStore();
- const allVectors = await vs.getVectorsByCollection(colId);
- const docVectors = allVectors.filter(v => v.docId === docId);
-
- await vs.deleteVectorsByDocument(colId, docId);
-
- const col = await vs.getCollection(colId);
- if (col) {
- col.chunkCount = Math.max(0, (col.chunkCount || 0) - docVectors.length);
- col.docCount = Math.max(0, (col.docCount || 0) - 1);
- await vs.updateCollection(col);
- }
-}
-
-/**
- * 获取集合中的文档列表(去重)
- */
-export async function getDocumentsInCollection(colId) {
- const vs = await initVectorStore();
- const vectors = await vs.getVectorsByCollection(colId);
- const docMap = new Map();
- for (const v of vectors) {
- if (!docMap.has(v.docId)) {
- docMap.set(v.docId, {
- docId: v.docId,
- filename: v.filename,
- chunkCount: 0
- });
- }
- docMap.get(v.docId).chunkCount++;
- }
- return Array.from(docMap.values());
-}
-
-/**
- * RAG 检索:查询 → 嵌入 → 相似度搜索 → 返回上下文文本
- * @param {string} query - 用户查询
- * @param {string} colId - 集合 ID
- * @param {number} topK - 返回最相关的 K 条
- * @returns {{ context: string, results: Array }}
- */
-export async function retrieveContext(query, colId, topK = 5) {
- const vs = await initVectorStore();
- const col = await vs.getCollection(colId);
- if (!col) throw new Error('知识库集合不存在');
-
- const embedModel = col.embeddingModel;
- if (!embedModel) throw new Error('未配置嵌入模型');
-
- // 嵌入查询
- const queryEmbedding = await embedText(query, embedModel);
-
- // 向量检索
- const results = await vs.search(colId, queryEmbedding, topK);
- if (results.length === 0) return { context: '', results: [] };
-
- // 构造上下文
- const contextParts = results.map((r, i) =>
- `[来源 ${i + 1}: ${r.filename}]\n${r.text}`
- );
- const context = contextParts.join('\n\n---\n\n');
-
- return { context, results };
-}
-
-/**
- * 构造 RAG 增强的系统提示词
- */
-export function buildRagSystemPrompt(context, originalSystemPrompt = '') {
- const ragPrompt = `你是一个知识库问答助手。以下是检索到的相关文档片段,请基于这些内容回答用户的问题。如果检索到的内容无法回答问题,请如实说明。
-
-=== 检索到的相关内容 ===
-${context}
-=== 内容结束 ===
-
-请基于以上内容回答用户的问题。引用时请注明来源编号(如"来源 1")。`;
-
- if (originalSystemPrompt) {
- return originalSystemPrompt + '\n\n' + ragPrompt;
- }
- return ragPrompt;
-}
diff --git a/js/sanitizer.js b/js/sanitizer.js
deleted file mode 100644
index 66e5389..0000000
--- a/js/sanitizer.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * Sanitizer - HTML 净化器
- * 基于白名单策略,防御 XSS 攻击
- */
-
-export const SAFE_URI_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:', 'ftp:', 'ftps:', 'xmpp:']);
-export const SAFE_DATA_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml']);
-
-const ALLOWED_TAGS = new Set([
- 'a', 'abbr', 'b', 'blockquote', 'br', 'code', 'del', 'details', 'div',
- 'dl', 'dt', 'dd', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr',
- 'i', 'img', 'ins', 'kbd', 'li', 'ol', 'p', 'pre', 'q', 'rp', 'rt',
- 'ruby', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub',
- 'summary', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
- 'tr', 'u', 'ul', 'var', 'input',
-]);
-
-const ALLOWED_ATTRS = new Set([
- 'href', 'src', 'alt', 'title', 'width', 'height', 'align',
- 'target', 'rel', 'class', 'id', 'type', 'checked', 'disabled',
- 'start', 'value', 'scope', 'rowspan', 'colspan', 'lang',
-]);
-
-const DANGEROUS_PREFIXES = ['on', 'xlink:href', 'xmlns:'];
-
-/**
- * 净化 HTML 字符串,移除危险标签和属性
- * @param {string} html - 原始 HTML
- * @returns {string} 净化后的 HTML
- */
-export function sanitize(html) {
- try {
- const template = document.createElement('template');
- template.innerHTML = html;
- const root = template.content;
-
- // 删除注释节点
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
- const comments = [];
- while (walker.nextNode()) comments.push(walker.currentNode);
- comments.forEach(c => c.remove());
-
- // 遍历所有元素(先收集再处理,避免动态集合问题)
- const elements = [...root.querySelectorAll('*')];
- for (const el of elements) {
- if (!el.parentNode) continue;
-
- const tag = el.tagName.toLowerCase();
-
- if (!ALLOWED_TAGS.has(tag)) {
- while (el.firstChild) el.parentNode.insertBefore(el.firstChild, el);
- el.remove();
- continue;
- }
-
- // 清理属性
- for (const attr of [...el.attributes]) {
- const name = attr.name.toLowerCase();
-
- if (DANGEROUS_PREFIXES.some(p => name.startsWith(p))) {
- el.removeAttribute(attr.name);
- continue;
- }
-
- if (!ALLOWED_ATTRS.has(name)) {
- el.removeAttribute(attr.name);
- continue;
- }
-
- // URI 协议检查
- if ((name === 'href' || name === 'src' || name === 'action') && attr.value) {
- const val = attr.value.trim().toLowerCase();
- if (/^(javascript|vbscript|data|file):/i.test(val)) {
- const mimeMatch = val.match(/^data:([^;,]+)/i);
- if (!mimeMatch || !SAFE_DATA_TYPES.has(mimeMatch[1].toLowerCase())) {
- el.removeAttribute(attr.name);
- }
- }
- }
- }
- }
-
- return root.innerHTML;
- } catch (e) {
- console.warn('[Sanitizer] 净化失败,返回原始文本:', e);
- return html;
- }
-}
diff --git a/js/state.js b/js/state.js
deleted file mode 100644
index c2855a6..0000000
--- a/js/state.js
+++ /dev/null
@@ -1,205 +0,0 @@
-/**
- * State - 轻量级响应式状态管理
- *
- * 优化点:
- * 1. 结构化浅比较:对象按 key 数量 + 逐 key 严格比较,数组按长度 + 逐元素
- * 2. update():函数式更新,自动比较
- * 3. setIn():路径式嵌套更新,沿途浅拷贝确保引用变化
- * 4. batch():批量更新,合并通知
- */
-
-// ── 结构化浅比较 ──
-function shallowEqual(a, b) {
- if (a === b) return true;
- if (a == null || b == null) return a === b;
- if (typeof a !== 'object' || typeof b !== 'object') return false;
-
- // 数组:长度 + 逐元素
- if (Array.isArray(a) && Array.isArray(b)) {
- if (a.length !== b.length) return false;
- for (let i = 0; i < a.length; i++) {
- if (a[i] !== b[i]) return false;
- }
- return true;
- }
-
- // 混合类型
- if (Array.isArray(a) !== Array.isArray(b)) return false;
-
- // 对象:key 数量 + 逐 key
- const keysA = Object.keys(a);
- const keysB = Object.keys(b);
- if (keysA.length !== keysB.length) return false;
- for (const key of keysA) {
- if (!Object.prototype.hasOwnProperty.call(b, key) || a[key] !== b[key]) return false;
- }
- return true;
-}
-
-class AppState {
- constructor() {
- this._state = {};
- this._listeners = new Map();
- this._batching = false;
- this._batchQueue = null;
- }
-
- /**
- * 设置状态值,使用结构化浅比较判断变更
- */
- set(key, value) {
- const old = this._state[key];
- this._state[key] = value;
-
- if (!shallowEqual(old, value)) {
- if (this._batching) {
- this._batchQueue.add(key);
- } else {
- this._notify(key, value, old);
- }
- }
- }
-
- /**
- * 函数式更新:基于当前值计算新值,自动比较
- *
- * state.update('currentSession', session => ({
- * ...session,
- * messages: [...session.messages, newMsg],
- * updatedAt: Date.now()
- * }));
- */
- update(key, updater) {
- const old = this._state[key];
- const value = updater(old);
- this._state[key] = value;
-
- if (!shallowEqual(old, value)) {
- if (this._batching) {
- this._batchQueue.add(key);
- } else {
- this._notify(key, value, old);
- }
- }
- }
-
- /**
- * 路径式嵌套更新:按 path 更新嵌套属性,沿途浅拷贝确保引用变化
- *
- * state.setIn('currentSession', ['messages', 0, 'content'], '新内容');
- */
- setIn(key, path, value) {
- if (!path || path.length === 0) {
- this.set(key, value);
- return;
- }
-
- const old = this._state[key];
- if (old == null || typeof old !== 'object') {
- console.warn(`[State] setIn 失败: ${key} 不是对象`);
- return;
- }
-
- const newRoot = Array.isArray(old) ? [...old] : { ...old };
- let current = newRoot;
-
- for (let i = 0; i < path.length - 1; i++) {
- const segment = path[i];
- const next = current[segment];
- if (next == null || typeof next !== 'object') {
- const isIndex = typeof path[i + 1] === 'number';
- current[segment] = isIndex ? [] : {};
- } else {
- current[segment] = Array.isArray(next) ? [...next] : { ...next };
- }
- current = current[segment];
- }
-
- current[path[path.length - 1]] = value;
-
- this._state[key] = newRoot;
- if (!shallowEqual(old, newRoot)) {
- if (this._batching) {
- this._batchQueue.add(key);
- } else {
- this._notify(key, newRoot, old);
- }
- }
- }
-
- /**
- * 批量更新:合并多次 set 为一次通知
- *
- * state.batch(() => {
- * state.set('isStreaming', false);
- * state.update('currentSession', s => ({ ...s, updatedAt: Date.now() }));
- * });
- */
- batch(fn) {
- this._batching = true;
- this._batchQueue = new Set();
- try {
- fn();
- } finally {
- const keys = this._batchQueue;
- this._batching = false;
- this._batchQueue = null;
- for (const k of keys) {
- this._notify(k, this._state[k], undefined);
- }
- }
- }
-
- _notify(key, value, old) {
- if (!this._listeners.has(key)) return;
- for (const cb of this._listeners.get(key)) {
- try { cb(value, old); } catch (e) {
- console.error(`[State] 监听器错误 (${key}):`, e);
- }
- }
- }
-
- /**
- * 获取状态值
- */
- get(key, defaultValue = undefined) {
- return key in this._state ? this._state[key] : defaultValue;
- }
-
- /**
- * 订阅状态变更
- * @returns {function} 取消订阅函数
- */
- on(key, callback) {
- if (!this._listeners.has(key)) {
- this._listeners.set(key, new Set());
- }
- this._listeners.get(key).add(callback);
- return () => this._listeners.get(key)?.delete(callback);
- }
-
- /**
- * 批量设置初始状态
- */
- init(initial) {
- Object.assign(this._state, initial);
- }
-}
-
-// 单例
-export const state = new AppState();
-
-export const KEYS = {
- DB: 'db',
- API: 'api',
- CURRENT_SESSION: 'currentSession',
- IS_STREAMING: 'isStreaming',
- IS_HISTORY_VIEW: 'isHistoryView',
- PENDING_IMAGES: 'pendingImages',
- ABORT_CONTROLLER: 'abortController',
- SYSTEM_PROMPT: 'systemPrompt',
- SYSTEM_PROMPT_ENABLED: 'systemPromptEnabled',
- NUM_CTX: 'numCtx',
- HISTORY_PAGE: 'historyPage',
- HISTORY_SEARCH: 'historySearchQuery',
-};
diff --git a/js/utils.js b/js/utils.js
deleted file mode 100644
index 5bb86be..0000000
--- a/js/utils.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/**
- * Utils - 通用工具函数
- */
-
-/** 生成唯一 ID */
-export function generateId() {
- return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
-}
-
-/** 格式化时间戳 */
-export function formatTime(ts) {
- const d = new Date(ts);
- const pad = n => String(n).padStart(2, '0');
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
-}
-
-/** 截断文本 */
-export function truncate(str, len = 50) {
- if (!str) return '';
- return str.length > len ? str.substring(0, len) + '...' : str;
-}
-
-/** 防抖 */
-export function debounce(fn, ms = 300) {
- let timer;
- return (...args) => {
- clearTimeout(timer);
- timer = setTimeout(() => fn(...args), ms);
- };
-}
-
-/** 格式化字节大小 */
-export function formatSize(bytes) {
- if (!bytes) return '';
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
- let i = 0;
- let size = bytes;
- while (size >= 1024 && i < units.length - 1) {
- size /= 1024;
- i++;
- }
- return `${size.toFixed(1)} ${units[i]}`;
-}
-
-/** HTML 转义 */
-export function escapeHtml(str) {
- const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
- return String(str).replace(/[&<>"']/g, (c) => map[c]);
-}
-
-/** 下载文件 */
-export function downloadFile(filename, content, type) {
- const blob = new Blob([content], { type: type + ';charset=utf-8' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = filename;
- a.click();
- URL.revokeObjectURL(url);
-}
-
-/** File → Base64(去前缀) */
-export function fileToBase64(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => {
- const base64 = reader.result.split(',')[1];
- resolve(base64);
- };
- reader.onerror = reject;
- reader.readAsDataURL(file);
- });
-}
-
-/** File → 文本内容 */
-export function fileToText(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => resolve(reader.result);
- reader.onerror = reject;
- reader.readAsText(file);
- });
-}
-
-/** 根据文件名检测语言标识(用于 Markdown 代码块) */
-export function detectLanguage(filename) {
- const ext = filename.split('.').pop().toLowerCase();
- const map = {
- py: 'python', pyw: 'python', pyi: 'python',
- js: 'javascript', mjs: 'javascript', cjs: 'javascript',
- ts: 'typescript', tsx: 'tsx', jsx: 'jsx',
- java: 'java', class: 'java',
- c: 'c', h: 'c',
- cpp: 'cpp', cc: 'cpp', cxx: 'cpp', hpp: 'cpp',
- go: 'go', rs: 'rust', rb: 'ruby', php: 'php',
- sh: 'bash', bash: 'bash', zsh: 'bash', fish: 'bash',
- sql: 'sql', html: 'html', htm: 'html', css: 'css',
- json: 'json', jsonl: 'json',
- xml: 'xml', svg: 'svg',
- yaml: 'yaml', yml: 'yaml', toml: 'toml',
- ini: 'ini', cfg: 'ini', conf: 'ini',
- md: 'markdown', markdown: 'markdown', rst: 'rst',
- txt: '', csv: 'csv', tsv: 'csv', log: '',
- swift: 'swift', kt: 'kotlin', kts: 'kotlin',
- scala: 'scala', lua: 'lua', pl: 'perl', r: 'r',
- m: 'matlab', mm: 'objectivec',
- cs: 'csharp', fs: 'fsharp', vb: 'vbnet',
- ex: 'elixir', exs: 'elixir', erl: 'erlang',
- hs: 'haskell', clj: 'clojure', lisp: 'lisp',
- dockerfile: 'dockerfile', makefile: 'makefile',
- diff: 'diff', patch: 'diff',
- };
- return map[ext] ?? ext;
-}
diff --git a/js/vector-store.js b/js/vector-store.js
deleted file mode 100644
index b0d0da1..0000000
--- a/js/vector-store.js
+++ /dev/null
@@ -1,449 +0,0 @@
-/**
- * VectorStore - 向量存储与相似度检索
- *
- * 优化:IVF 倒排索引
- * - 小数据集 (N < MIN_IVF_SIZE):自动降级为暴力搜索
- * - 大数据集:K-Means 聚类 + nprobe 近邻搜索
- * - 索引在 addVectors() 后自动构建,持久化到 IndexedDB
- */
-
-const MIN_IVF_SIZE = 200; // 低于此数量不建索引,直接暴力搜
-const DEFAULT_K = 20; // 聚类中心数上限
-const DEFAULT_NPROBE = 5; // 搜索时探测的聚类数(覆盖 ~25% 数据)
-const KMEANS_ITERS = 10; // K-Means 迭代次数
-
-export class VectorStore {
- constructor(dbName = 'metona-ollama-vectors') {
- this.dbName = dbName;
- this.db = null;
-
- // 内存索引缓存 { colId → { centroids, invertedLists, version } }
- this._indexCache = new Map();
-
- // 向量内存缓存 { colId → Map }
- this._vectorCache = new Map();
- }
-
- async init() {
- return new Promise((resolve, reject) => {
- const req = indexedDB.open(this.dbName, 2);
- req.onerror = () => reject(req.error);
- req.onsuccess = () => { this.db = req.result; resolve(); };
- req.onupgradeneeded = (e) => {
- const db = e.target.result;
- if (!db.objectStoreNames.contains('vectors')) {
- const store = db.createObjectStore('vectors', { keyPath: 'id' });
- store.createIndex('collectionId', 'collectionId', { unique: false });
- }
- if (!db.objectStoreNames.contains('collections')) {
- db.createObjectStore('collections', { keyPath: 'id' });
- }
- if (!db.objectStoreNames.contains('indexes')) {
- db.createObjectStore('indexes', { keyPath: 'collectionId' });
- }
- };
- });
- }
-
- _tx(store, mode = 'readonly') {
- return this.db.transaction(store, mode).objectStore(store);
- }
-
- // ═══════════════════════════════════════════
- // 集合管理
- // ═══════════════════════════════════════════
-
- async createCollection(name, embeddingModel = '') {
- const col = {
- id: `kb_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
- name, embeddingModel,
- docCount: 0, chunkCount: 0,
- createdAt: Date.now(), updatedAt: Date.now()
- };
- return new Promise((resolve, reject) => {
- const req = this._tx('collections', 'readwrite').put(col);
- req.onsuccess = () => resolve(col);
- req.onerror = () => reject(req.error);
- });
- }
-
- async getCollections() {
- return new Promise((resolve, reject) => {
- const req = this._tx('collections').getAll();
- req.onsuccess = () => resolve(req.result || []);
- req.onerror = () => reject(req.error);
- });
- }
-
- async getCollection(id) {
- return new Promise((resolve, reject) => {
- const req = this._tx('collections').get(id);
- req.onsuccess = () => resolve(req.result || null);
- req.onerror = () => reject(req.error);
- });
- }
-
- async updateCollection(col) {
- col.updatedAt = Date.now();
- return new Promise((resolve, reject) => {
- const req = this._tx('collections', 'readwrite').put(col);
- req.onsuccess = () => resolve();
- req.onerror = () => reject(req.error);
- });
- }
-
- async deleteCollection(colId) {
- const vectors = await this.getVectorsByCollection(colId);
- await new Promise((resolve, reject) => {
- const tx = this.db.transaction(['vectors', 'collections', 'indexes'], 'readwrite');
- const vStore = tx.objectStore('vectors');
- for (const v of vectors) vStore.delete(v.id);
- tx.objectStore('collections').delete(colId);
- tx.objectStore('indexes').delete(colId);
- tx.oncomplete = () => resolve();
- tx.onerror = () => reject(tx.error);
- });
- this._indexCache.delete(colId);
- this._vectorCache.delete(colId);
- }
-
- // ═══════════════════════════════════════════
- // 向量操作
- // ═══════════════════════════════════════════
-
- async addVectors(items) {
- if (items.length === 0) return;
-
- await new Promise((resolve, reject) => {
- const tx = this.db.transaction('vectors', 'readwrite');
- const store = tx.objectStore('vectors');
- for (const item of items) store.put(item);
- tx.oncomplete = () => resolve();
- tx.onerror = () => reject(tx.error);
- });
-
- // 更新内存缓存
- const colId = items[0].collectionId;
- if (!this._vectorCache.has(colId)) {
- this._vectorCache.set(colId, new Map());
- }
- const cache = this._vectorCache.get(colId);
- for (const item of items) cache.set(item.id, item);
-
- // 标记索引需重建
- this._indexCache.delete(colId);
- }
-
- async getVectorsByCollection(colId) {
- if (this._vectorCache.has(colId)) {
- return Array.from(this._vectorCache.get(colId).values());
- }
-
- const vectors = await new Promise((resolve, reject) => {
- const idx = this._tx('vectors').index('collectionId');
- const req = idx.getAll(colId);
- req.onsuccess = () => resolve(req.result || []);
- req.onerror = () => reject(req.error);
- });
-
- const cache = new Map();
- for (const v of vectors) cache.set(v.id, v);
- this._vectorCache.set(colId, cache);
-
- return vectors;
- }
-
- async deleteVectorsByCollection(colId) {
- const vectors = await this.getVectorsByCollection(colId);
- await new Promise((resolve, reject) => {
- const tx = this.db.transaction('vectors', 'readwrite');
- const store = tx.objectStore('vectors');
- for (const v of vectors) store.delete(v.id);
- tx.oncomplete = () => resolve();
- tx.onerror = () => reject(tx.error);
- });
- this._indexCache.delete(colId);
- this._vectorCache.delete(colId);
- }
-
- async deleteVectorsByDocument(colId, docId) {
- const vectors = await this.getVectorsByCollection(colId);
- const docVectors = vectors.filter(v => v.docId === docId);
- await new Promise((resolve, reject) => {
- const tx = this.db.transaction('vectors', 'readwrite');
- const store = tx.objectStore('vectors');
- for (const v of docVectors) store.delete(v.id);
- tx.oncomplete = () => resolve();
- tx.onerror = () => reject(tx.error);
- });
- if (this._vectorCache.has(colId)) {
- const cache = this._vectorCache.get(colId);
- for (const v of docVectors) cache.delete(v.id);
- }
- this._indexCache.delete(colId);
- }
-
- // ═══════════════════════════════════════════
- // 搜索:自动选择暴力 / IVF
- // ═══════════════════════════════════════════
-
- async search(colId, queryEmbedding, topK = 5) {
- const vectors = await this.getVectorsByCollection(colId);
- if (vectors.length === 0) return [];
-
- // 小数据集 → 暴力搜索
- if (vectors.length < MIN_IVF_SIZE) {
- return this._bruteForceSearch(vectors, queryEmbedding, topK);
- }
-
- // 大数据集 → IVF 搜索
- const index = await this._getIndex(colId, vectors);
- if (!index) {
- return this._bruteForceSearch(vectors, queryEmbedding, topK);
- }
-
- return this._ivfSearch(vectors, index, queryEmbedding, topK);
- }
-
- _bruteForceSearch(vectors, queryEmbedding, topK) {
- return vectors
- .map(v => ({ ...v, score: VectorStore.cosineSimilarity(queryEmbedding, v.embedding) }))
- .sort((a, b) => b.score - a.score)
- .slice(0, topK);
- }
-
- _ivfSearch(vectors, index, queryEmbedding, topK) {
- const { centroids, invertedLists } = index;
-
- // 1. 找最近的 nprobe 个聚类中心
- const clusterScores = centroids.map((c, i) => ({
- id: i,
- score: VectorStore.cosineSimilarity(queryEmbedding, c)
- }));
- clusterScores.sort((a, b) => b.score - a.score);
-
- const nprobe = Math.min(DEFAULT_NPROBE, centroids.length);
- const targetClusters = clusterScores.slice(0, nprobe);
-
- // 2. 收集候选向量 ID
- const candidateIds = new Set();
- for (const { id: clusterId } of targetClusters) {
- const list = invertedLists.get(clusterId);
- if (list) for (const id of list) candidateIds.add(id);
- }
-
- // 3. 候选向量构建快速查找(避免逐个 find)
- const vectorMap = new Map();
- for (const v of vectors) vectorMap.set(v.id, v);
-
- const candidates = [];
- for (const id of candidateIds) {
- const v = vectorMap.get(id);
- if (v) candidates.push(v);
- }
-
- return candidates
- .map(v => ({ ...v, score: VectorStore.cosineSimilarity(queryEmbedding, v.embedding) }))
- .sort((a, b) => b.score - a.score)
- .slice(0, topK);
- }
-
- // ═══════════════════════════════════════════
- // IVF 索引
- // ═══════════════════════════════════════════
-
- async _getIndex(colId, vectors) {
- // 内存缓存
- if (this._indexCache.has(colId)) {
- const cached = this._indexCache.get(colId);
- if (cached.version === this._indexVersion(vectors)) {
- return cached;
- }
- }
-
- // IndexedDB 缓存
- const saved = await new Promise((resolve, reject) => {
- const req = this._tx('indexes').get(colId);
- req.onsuccess = () => resolve(req.result || null);
- req.onerror = () => reject(req.error);
- });
-
- if (saved && saved.version === this._indexVersion(vectors)) {
- // 反序列化 invertedLists(IndexedDB 不存 Map,存的是对象)
- if (!(saved.invertedLists instanceof Map)) {
- saved.invertedLists = new Map(Object.entries(saved.invertedLists || {}));
- }
- this._indexCache.set(colId, saved);
- return saved;
- }
-
- // 重建
- console.log(`[VectorStore] 构建 IVF 索引: ${colId} (${vectors.length} 向量)`);
- const index = this._buildIVFIndex(vectors);
- index.collectionId = colId;
- index.version = this._indexVersion(vectors);
-
- // 持久化(Map → 普通对象)
- const toSave = {
- collectionId: index.collectionId,
- version: index.version,
- centroids: index.centroids,
- invertedLists: Object.fromEntries(index.invertedLists)
- };
- await new Promise((resolve, reject) => {
- const req = this._tx('indexes', 'readwrite').put(toSave);
- req.onsuccess = () => resolve();
- req.onerror = () => reject(req.error);
- });
-
- this._indexCache.set(colId, index);
- return index;
- }
-
- _indexVersion(vectors) {
- if (vectors.length === 0) return '0';
- return `${vectors.length}_${vectors[0]?.id || ''}_${vectors[vectors.length - 1]?.id || ''}`;
- }
-
- /**
- * K-Means 聚类构建 IVF 索引
- */
- _buildIVFIndex(vectors) {
- const N = vectors.length;
- const dim = vectors[0].embedding.length;
-
- const K = Math.max(2, Math.min(DEFAULT_K, Math.floor(N / 10)));
-
- // 1. K-Means++ 初始化
- const centroids = this._kmeansPPInit(vectors, K, dim);
-
- // 2. K-Means 迭代
- const assignments = new Array(N);
- for (let iter = 0; iter < KMEANS_ITERS; iter++) {
- // E-step
- for (let i = 0; i < N; i++) {
- let bestCluster = 0;
- let bestScore = -Infinity;
- for (let k = 0; k < K; k++) {
- const score = VectorStore.cosineSimilarity(vectors[i].embedding, centroids[k]);
- if (score > bestScore) {
- bestScore = score;
- bestCluster = k;
- }
- }
- assignments[i] = bestCluster;
- }
-
- // M-step
- const newCentroids = Array.from({ length: K }, () => new Array(dim).fill(0));
- const counts = new Array(K).fill(0);
-
- for (let i = 0; i < N; i++) {
- const k = assignments[i];
- counts[k]++;
- const emb = vectors[i].embedding;
- for (let d = 0; d < dim; d++) {
- newCentroids[k][d] += emb[d];
- }
- }
-
- for (let k = 0; k < K; k++) {
- if (counts[k] > 0) {
- for (let d = 0; d < dim; d++) {
- newCentroids[k][d] /= counts[k];
- }
- this._normalize(newCentroids[k]);
- } else {
- const randIdx = Math.floor(Math.random() * N);
- newCentroids[k] = [...vectors[randIdx].embedding];
- }
- }
-
- // 收敛检测
- let converged = true;
- for (let k = 0; k < K; k++) {
- if (VectorStore.cosineSimilarity(centroids[k], newCentroids[k]) < 0.999) {
- converged = false;
- break;
- }
- }
-
- for (let k = 0; k < K; k++) {
- centroids[k] = newCentroids[k];
- }
-
- if (converged) break;
- }
-
- // 3. 构建倒排链表
- const invertedLists = new Map();
- for (let i = 0; i < N; i++) {
- const k = assignments[i];
- if (!invertedLists.has(k)) invertedLists.set(k, []);
- invertedLists.get(k).push(vectors[i].id);
- }
-
- return { centroids, invertedLists };
- }
-
- /**
- * K-Means++ 初始化
- */
- _kmeansPPInit(vectors, K, dim) {
- const centroids = [];
- const first = Math.floor(Math.random() * vectors.length);
- centroids.push([...vectors[first].embedding]);
-
- for (let k = 1; k < K; k++) {
- const dists = vectors.map(v => {
- let minDist = Infinity;
- for (const c of centroids) {
- const sim = VectorStore.cosineSimilarity(v.embedding, c);
- const dist = 1 - sim;
- if (dist < minDist) minDist = dist;
- }
- return minDist;
- });
-
- const total = dists.reduce((s, d) => s + d, 0);
- if (total === 0) {
- centroids.push([...vectors[Math.floor(Math.random() * vectors.length)].embedding]);
- continue;
- }
-
- let r = Math.random() * total;
- for (let i = 0; i < vectors.length; i++) {
- r -= dists[i];
- if (r <= 0) {
- centroids.push([...vectors[i].embedding]);
- break;
- }
- }
- }
- return centroids;
- }
-
- /** 归一化向量(in-place) */
- _normalize(vec) {
- let norm = 0;
- for (let i = 0; i < vec.length; i++) norm += vec[i] * vec[i];
- norm = Math.sqrt(norm);
- if (norm > 0) for (let i = 0; i < vec.length; i++) vec[i] /= norm;
- }
-
- // ═══════════════════════════════════════════
- // 工具
- // ═══════════════════════════════════════════
-
- static cosineSimilarity(a, b) {
- if (!a || !b || a.length !== b.length) return 0;
- let dot = 0, normA = 0, normB = 0;
- for (let i = 0; i < a.length; i++) {
- dot += a[i] * b[i];
- normA += a[i] * a[i];
- normB += b[i] * b[i];
- }
- const denom = Math.sqrt(normA) * Math.sqrt(normB);
- return denom === 0 ? 0 : dot / denom;
- }
-}
diff --git a/manifest.json b/manifest.json
deleted file mode 100644
index ec9255e..0000000
--- a/manifest.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "Metona Ollama Client",
- "short_name": "Ollama",
- "description": "基于原生 JavaScript 的 Ollama AI 客户端 - 流式聊天、多模态、历史管理",
- "start_url": "./index.html",
- "display": "standalone",
- "background_color": "#0a0a1a",
- "theme_color": "#0a0a1a",
- "orientation": "any",
- "icons": [
- {
- "src": "data:image/svg+xml,🦙 ",
- "sizes": "any",
- "type": "image/svg+xml",
- "purpose": "any maskable"
- }
- ],
- "categories": ["productivity", "utilities"]
-}
diff --git a/package-lock.json b/package-lock.json
index 91fdd8d..d011096 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,20 +1,25 @@
{
"name": "metona-ollama-desktop",
- "version": "1.0.0",
+ "version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "metona-ollama-desktop",
- "version": "1.0.0",
+ "version": "2.0.0",
"license": "MIT",
"devDependencies": {
+ "@types/node": "^20.17.0",
"electron": "^33.4.11",
- "electron-builder": "^25.0.0"
+ "electron-builder": "^25.1.8",
+ "typescript": "^5.7.0",
+ "vite": "^5.4.0"
}
},
"node_modules/@develar/schema-utils": {
"version": "2.6.5",
+ "resolved": "https://registry.npmmirror.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
+ "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -31,6 +36,8 @@
},
"node_modules/@electron/asar": {
"version": "3.4.1",
+ "resolved": "https://registry.npmmirror.com/@electron/asar/-/asar-3.4.1.tgz",
+ "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -47,11 +54,15 @@
},
"node_modules/@electron/asar/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/@electron/asar/node_modules/brace-expansion": {
"version": "1.1.13",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -61,6 +72,8 @@
},
"node_modules/@electron/asar/node_modules/minimatch": {
"version": "3.1.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -72,6 +85,8 @@
},
"node_modules/@electron/get": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/@electron/get/-/get-2.0.3.tgz",
+ "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -92,6 +107,8 @@
},
"node_modules/@electron/notarize": {
"version": "2.5.0",
+ "resolved": "https://registry.npmmirror.com/@electron/notarize/-/notarize-2.5.0.tgz",
+ "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -105,6 +122,8 @@
},
"node_modules/@electron/notarize/node_modules/fs-extra": {
"version": "9.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -119,6 +138,8 @@
},
"node_modules/@electron/notarize/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -130,6 +151,8 @@
},
"node_modules/@electron/notarize/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -138,6 +161,8 @@
},
"node_modules/@electron/osx-sign": {
"version": "1.3.1",
+ "resolved": "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-1.3.1.tgz",
+ "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -158,6 +183,8 @@
},
"node_modules/@electron/osx-sign/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -171,6 +198,8 @@
},
"node_modules/@electron/osx-sign/node_modules/isbinaryfile": {
"version": "4.0.10",
+ "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+ "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -182,6 +211,8 @@
},
"node_modules/@electron/osx-sign/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -193,6 +224,8 @@
},
"node_modules/@electron/osx-sign/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -201,6 +234,8 @@
},
"node_modules/@electron/rebuild": {
"version": "3.6.1",
+ "resolved": "https://registry.npmmirror.com/@electron/rebuild/-/rebuild-3.6.1.tgz",
+ "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -228,6 +263,8 @@
},
"node_modules/@electron/rebuild/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -241,6 +278,8 @@
},
"node_modules/@electron/rebuild/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -252,6 +291,8 @@
},
"node_modules/@electron/rebuild/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -263,6 +304,8 @@
},
"node_modules/@electron/rebuild/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -271,6 +314,8 @@
},
"node_modules/@electron/universal": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/@electron/universal/-/universal-2.0.1.tgz",
+ "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -288,11 +333,15 @@
},
"node_modules/@electron/universal/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/@electron/universal/node_modules/brace-expansion": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -301,6 +350,8 @@
},
"node_modules/@electron/universal/node_modules/fs-extra": {
"version": "11.3.4",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.4.tgz",
+ "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -314,6 +365,8 @@
},
"node_modules/@electron/universal/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -325,6 +378,8 @@
},
"node_modules/@electron/universal/node_modules/minimatch": {
"version": "9.0.9",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -339,19 +394,416 @@
},
"node_modules/@electron/universal/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.0.0"
}
},
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@gar/promisify": {
"version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz",
+ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
+ "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -368,6 +820,8 @@
},
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
"version": "6.2.2",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -379,6 +833,8 @@
},
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
"version": "6.2.3",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -390,11 +846,15 @@
},
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
"version": "9.2.2",
+ "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true,
"license": "MIT"
},
"node_modules/@isaacs/cliui/node_modules/string-width": {
"version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -411,6 +871,8 @@
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.2.0",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -425,6 +887,8 @@
},
"node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
"version": "8.1.0",
+ "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -441,6 +905,8 @@
},
"node_modules/@malept/cross-spawn-promise": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
+ "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==",
"dev": true,
"funding": [
{
@@ -462,6 +928,8 @@
},
"node_modules/@malept/flatpak-bundler": {
"version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz",
+ "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -476,6 +944,8 @@
},
"node_modules/@malept/flatpak-bundler/node_modules/fs-extra": {
"version": "9.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -490,6 +960,8 @@
},
"node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -501,6 +973,8 @@
},
"node_modules/@malept/flatpak-bundler/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -509,6 +983,8 @@
},
"node_modules/@npmcli/fs": {
"version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-2.1.2.tgz",
+ "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -521,6 +997,8 @@
},
"node_modules/@npmcli/fs/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -532,6 +1010,9 @@
},
"node_modules/@npmcli/move-file": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/@npmcli/move-file/-/move-file-2.0.1.tgz",
+ "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
+ "deprecated": "This functionality has been moved to @npmcli/fs",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -544,6 +1025,8 @@
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
+ "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -551,8 +1034,360 @@
"node": ">=14"
}
},
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
+ "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz",
+ "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz",
+ "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz",
+ "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz",
+ "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz",
+ "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz",
+ "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz",
+ "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz",
+ "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz",
+ "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz",
+ "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz",
+ "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz",
+ "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz",
+ "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz",
+ "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz",
+ "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz",
+ "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz",
+ "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz",
+ "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz",
+ "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz",
+ "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz",
+ "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz",
+ "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz",
+ "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz",
+ "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
+ "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -564,6 +1399,8 @@
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
+ "resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -575,6 +1412,8 @@
},
"node_modules/@tootallnate/once": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -583,6 +1422,8 @@
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
+ "resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -594,14 +1435,25 @@
},
"node_modules/@types/debug": {
"version": "4.1.13",
+ "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/ms": "*"
}
},
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/fs-extra": {
"version": "9.0.13",
+ "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz",
+ "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -610,11 +1462,15 @@
},
"node_modules/@types/http-cache-semantics": {
"version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/keyv": {
"version": "3.1.4",
+ "resolved": "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz",
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -623,11 +1479,15 @@
},
"node_modules/@types/ms": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.39",
+ "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.39.tgz",
+ "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -636,6 +1496,8 @@
},
"node_modules/@types/plist": {
"version": "3.0.5",
+ "resolved": "https://registry.npmmirror.com/@types/plist/-/plist-3.0.5.tgz",
+ "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -646,6 +1508,8 @@
},
"node_modules/@types/responselike": {
"version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -654,12 +1518,16 @@
},
"node_modules/@types/verror": {
"version": "1.10.11",
+ "resolved": "https://registry.npmmirror.com/@types/verror/-/verror-1.10.11.tgz",
+ "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
+ "resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -669,6 +1537,8 @@
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.12",
+ "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
+ "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -677,16 +1547,22 @@
},
"node_modules/7zip-bin": {
"version": "5.2.0",
+ "resolved": "https://registry.npmmirror.com/7zip-bin/-/7zip-bin-5.2.0.tgz",
+ "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
"dev": true,
"license": "MIT"
},
"node_modules/abbrev": {
"version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"license": "ISC"
},
"node_modules/agent-base": {
"version": "7.1.4",
+ "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -695,6 +1571,8 @@
},
"node_modules/agentkeepalive": {
"version": "4.6.0",
+ "resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+ "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -706,6 +1584,8 @@
},
"node_modules/aggregate-error": {
"version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -718,6 +1598,8 @@
},
"node_modules/ajv": {
"version": "6.14.0",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -733,6 +1615,8 @@
},
"node_modules/ajv-keywords": {
"version": "3.5.2",
+ "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -741,6 +1625,8 @@
},
"node_modules/ansi-regex": {
"version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -749,6 +1635,8 @@
},
"node_modules/ansi-styles": {
"version": "4.3.0",
+ "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -763,11 +1651,15 @@
},
"node_modules/app-builder-bin": {
"version": "5.0.0-alpha.10",
+ "resolved": "https://registry.npmmirror.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz",
+ "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==",
"dev": true,
"license": "MIT"
},
"node_modules/app-builder-lib": {
"version": "25.1.8",
+ "resolved": "https://registry.npmmirror.com/app-builder-lib/-/app-builder-lib-25.1.8.tgz",
+ "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -814,6 +1706,8 @@
},
"node_modules/app-builder-lib/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -827,6 +1721,8 @@
},
"node_modules/app-builder-lib/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -838,6 +1734,8 @@
},
"node_modules/app-builder-lib/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -849,6 +1747,8 @@
},
"node_modules/app-builder-lib/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -857,11 +1757,15 @@
},
"node_modules/aproba": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.1.0.tgz",
+ "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
"dev": true,
"license": "ISC"
},
"node_modules/archiver": {
"version": "5.3.2",
+ "resolved": "https://registry.npmmirror.com/archiver/-/archiver-5.3.2.tgz",
+ "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -880,6 +1784,8 @@
},
"node_modules/archiver-utils": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-2.1.0.tgz",
+ "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -901,6 +1807,8 @@
},
"node_modules/archiver-utils/node_modules/readable-stream": {
"version": "2.3.8",
+ "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -916,12 +1824,16 @@
},
"node_modules/archiver-utils/node_modules/safe-buffer": {
"version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/archiver-utils/node_modules/string_decoder": {
"version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -931,6 +1843,9 @@
},
"node_modules/are-we-there-yet": {
"version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+ "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -943,11 +1858,15 @@
},
"node_modules/argparse": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/assert-plus": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -957,6 +1876,8 @@
},
"node_modules/astral-regex": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -966,11 +1887,15 @@
},
"node_modules/async": {
"version": "3.2.6",
+ "resolved": "https://registry.npmmirror.com/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"dev": true,
"license": "MIT"
},
"node_modules/async-exit-hook": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
+ "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -979,11 +1904,15 @@
},
"node_modules/asynckit": {
"version": "0.4.0",
+ "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/at-least-node": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true,
"license": "ISC",
"engines": {
@@ -992,6 +1921,8 @@
},
"node_modules/balanced-match": {
"version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1000,6 +1931,8 @@
},
"node_modules/base64-js": {
"version": "1.5.1",
+ "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [
{
@@ -1019,6 +1952,8 @@
},
"node_modules/bl": {
"version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1029,11 +1964,15 @@
},
"node_modules/bluebird": {
"version": "3.7.2",
+ "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true,
"license": "MIT"
},
"node_modules/bluebird-lst": {
"version": "1.0.9",
+ "resolved": "https://registry.npmmirror.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz",
+ "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1042,12 +1981,17 @@
},
"node_modules/boolean": {
"version": "3.2.0",
+ "resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz",
+ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/brace-expansion": {
"version": "5.0.5",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1059,6 +2003,8 @@
},
"node_modules/buffer": {
"version": "5.7.1",
+ "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dev": true,
"funding": [
{
@@ -1082,6 +2028,8 @@
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
+ "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1090,11 +2038,15 @@
},
"node_modules/buffer-from": {
"version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/builder-util": {
"version": "25.1.7",
+ "resolved": "https://registry.npmmirror.com/builder-util/-/builder-util-25.1.7.tgz",
+ "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1118,6 +2070,8 @@
},
"node_modules/builder-util-runtime": {
"version": "9.2.10",
+ "resolved": "https://registry.npmmirror.com/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz",
+ "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1130,6 +2084,8 @@
},
"node_modules/builder-util/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1143,6 +2099,8 @@
},
"node_modules/builder-util/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1154,6 +2112,8 @@
},
"node_modules/builder-util/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1162,6 +2122,8 @@
},
"node_modules/cacache": {
"version": "16.1.3",
+ "resolved": "https://registry.npmmirror.com/cacache/-/cacache-16.1.3.tgz",
+ "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1190,11 +2152,15 @@
},
"node_modules/cacache/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/cacache/node_modules/brace-expansion": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1203,6 +2169,9 @@
},
"node_modules/cacache/node_modules/glob": {
"version": "8.1.0",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1221,6 +2190,8 @@
},
"node_modules/cacache/node_modules/lru-cache": {
"version": "7.18.3",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -1229,6 +2200,8 @@
},
"node_modules/cacache/node_modules/minimatch": {
"version": "5.1.9",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1240,6 +2213,8 @@
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
+ "resolved": "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1248,6 +2223,8 @@
},
"node_modules/cacheable-request": {
"version": "7.0.4",
+ "resolved": "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1265,6 +2242,8 @@
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1277,6 +2256,8 @@
},
"node_modules/chalk": {
"version": "4.1.2",
+ "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1292,6 +2273,8 @@
},
"node_modules/chownr": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"dev": true,
"license": "ISC",
"engines": {
@@ -1300,11 +2283,15 @@
},
"node_modules/chromium-pickle-js": {
"version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
+ "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==",
"dev": true,
"license": "MIT"
},
"node_modules/ci-info": {
"version": "3.9.0",
+ "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
"dev": true,
"funding": [
{
@@ -1319,6 +2306,8 @@
},
"node_modules/clean-stack": {
"version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1327,6 +2316,8 @@
},
"node_modules/cli-cursor": {
"version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1338,6 +2329,8 @@
},
"node_modules/cli-spinners": {
"version": "2.9.2",
+ "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1349,6 +2342,8 @@
},
"node_modules/cli-truncate": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz",
+ "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1365,6 +2360,8 @@
},
"node_modules/cliui": {
"version": "8.0.1",
+ "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1378,6 +2375,8 @@
},
"node_modules/clone": {
"version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1386,6 +2385,8 @@
},
"node_modules/clone-response": {
"version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz",
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1397,6 +2398,8 @@
},
"node_modules/color-convert": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1408,11 +2411,15 @@
},
"node_modules/color-name": {
"version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/color-support": {
"version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true,
"license": "ISC",
"bin": {
@@ -1421,6 +2428,8 @@
},
"node_modules/combined-stream": {
"version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1432,6 +2441,8 @@
},
"node_modules/commander": {
"version": "5.1.0",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1440,6 +2451,8 @@
},
"node_modules/compare-version": {
"version": "0.1.2",
+ "resolved": "https://registry.npmmirror.com/compare-version/-/compare-version-0.1.2.tgz",
+ "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1448,6 +2461,8 @@
},
"node_modules/compress-commons": {
"version": "4.1.2",
+ "resolved": "https://registry.npmmirror.com/compress-commons/-/compress-commons-4.1.2.tgz",
+ "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1463,11 +2478,15 @@
},
"node_modules/concat-map": {
"version": "0.0.1",
+ "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true,
"license": "MIT"
},
"node_modules/config-file-ts": {
"version": "0.2.8-rc1",
+ "resolved": "https://registry.npmmirror.com/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz",
+ "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1477,11 +2496,15 @@
},
"node_modules/config-file-ts/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/config-file-ts/node_modules/brace-expansion": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1489,7 +2512,9 @@
}
},
"node_modules/config-file-ts/node_modules/glob": {
- "version": "10.5.0",
+ "version": "10.4.5",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1509,6 +2534,8 @@
},
"node_modules/config-file-ts/node_modules/minimatch": {
"version": "9.0.9",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1523,6 +2550,8 @@
},
"node_modules/config-file-ts/node_modules/minipass": {
"version": "7.1.3",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
@@ -1531,16 +2560,22 @@
},
"node_modules/console-control-strings": {
"version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
"dev": true,
"license": "ISC"
},
"node_modules/core-util-is": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true,
"license": "MIT"
},
"node_modules/crc": {
"version": "3.8.0",
+ "resolved": "https://registry.npmmirror.com/crc/-/crc-3.8.0.tgz",
+ "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1550,6 +2585,8 @@
},
"node_modules/crc-32": {
"version": "1.2.2",
+ "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
@@ -1562,6 +2599,8 @@
},
"node_modules/crc32-stream": {
"version": "4.0.3",
+ "resolved": "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-4.0.3.tgz",
+ "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1575,6 +2614,8 @@
},
"node_modules/cross-spawn": {
"version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1588,6 +2629,8 @@
},
"node_modules/debug": {
"version": "4.4.3",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1604,6 +2647,8 @@
},
"node_modules/decompress-response": {
"version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1618,6 +2663,8 @@
},
"node_modules/decompress-response/node_modules/mimic-response": {
"version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1629,6 +2676,8 @@
},
"node_modules/defaults": {
"version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1640,6 +2689,8 @@
},
"node_modules/defer-to-connect": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1648,6 +2699,8 @@
},
"node_modules/define-data-property": {
"version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1665,6 +2718,8 @@
},
"node_modules/define-properties": {
"version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1682,6 +2737,8 @@
},
"node_modules/delayed-stream": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1690,11 +2747,15 @@
},
"node_modules/delegates": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -1703,12 +2764,16 @@
},
"node_modules/detect-node": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/dir-compare": {
"version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz",
+ "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1718,11 +2783,15 @@
},
"node_modules/dir-compare/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/dir-compare/node_modules/brace-expansion": {
"version": "1.1.13",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1732,6 +2801,8 @@
},
"node_modules/dir-compare/node_modules/minimatch": {
"version": "3.1.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -1743,6 +2814,8 @@
},
"node_modules/dmg-builder": {
"version": "25.1.8",
+ "resolved": "https://registry.npmmirror.com/dmg-builder/-/dmg-builder-25.1.8.tgz",
+ "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1757,9 +2830,47 @@
"dmg-license": "^1.0.11"
}
},
- "node_modules/dmg-builder/node_modules/dmg-license": {
+ "node_modules/dmg-builder/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dmg-builder/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/dmg-builder/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/dmg-license": {
"version": "1.0.11",
- "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
+ "resolved": "https://registry.npmmirror.com/dmg-license/-/dmg-license-1.0.11.tgz",
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
"dev": true,
"license": "MIT",
@@ -1784,58 +2895,10 @@
"node": ">=8"
}
},
- "node_modules/dmg-builder/node_modules/fs-extra": {
- "version": "10.1.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/dmg-builder/node_modules/iconv-corefoundation": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
- "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "dependencies": {
- "cli-truncate": "^2.1.0",
- "node-addon-api": "^1.6.3"
- },
- "engines": {
- "node": "^8.11.2 || >=10"
- }
- },
- "node_modules/dmg-builder/node_modules/jsonfile": {
- "version": "6.2.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/dmg-builder/node_modules/universalify": {
- "version": "2.0.1",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/dotenv": {
"version": "16.6.1",
+ "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -1847,6 +2910,8 @@
},
"node_modules/dotenv-expand": {
"version": "11.0.7",
+ "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-11.0.7.tgz",
+ "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -1861,6 +2926,8 @@
},
"node_modules/dunder-proto": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1874,11 +2941,15 @@
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
+ "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true,
"license": "MIT"
},
"node_modules/ejs": {
"version": "3.1.10",
+ "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -1893,7 +2964,7 @@
},
"node_modules/electron": {
"version": "33.4.11",
- "resolved": "https://registry.npmjs.org/electron/-/electron-33.4.11.tgz",
+ "resolved": "https://registry.npmmirror.com/electron/-/electron-33.4.11.tgz",
"integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==",
"dev": true,
"hasInstallScript": true,
@@ -1912,6 +2983,8 @@
},
"node_modules/electron-builder": {
"version": "25.1.8",
+ "resolved": "https://registry.npmmirror.com/electron-builder/-/electron-builder-25.1.8.tgz",
+ "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1936,6 +3009,8 @@
},
"node_modules/electron-builder-squirrel-windows": {
"version": "25.1.8",
+ "resolved": "https://registry.npmmirror.com/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz",
+ "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1948,6 +3023,8 @@
},
"node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1962,6 +3039,8 @@
},
"node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1974,6 +3053,8 @@
},
"node_modules/electron-builder-squirrel-windows/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -1983,6 +3064,8 @@
},
"node_modules/electron-builder/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1996,6 +3079,8 @@
},
"node_modules/electron-builder/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2007,6 +3092,8 @@
},
"node_modules/electron-builder/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2015,6 +3102,8 @@
},
"node_modules/electron-publish": {
"version": "25.1.7",
+ "resolved": "https://registry.npmmirror.com/electron-publish/-/electron-publish-25.1.7.tgz",
+ "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2029,6 +3118,8 @@
},
"node_modules/electron-publish/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2042,6 +3133,8 @@
},
"node_modules/electron-publish/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2053,6 +3146,8 @@
},
"node_modules/electron-publish/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2061,11 +3156,15 @@
},
"node_modules/emoji-regex": {
"version": "8.0.0",
+ "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/encoding": {
"version": "0.1.13",
+ "resolved": "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -2075,6 +3174,8 @@
},
"node_modules/end-of-stream": {
"version": "1.4.5",
+ "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2083,6 +3184,8 @@
},
"node_modules/env-paths": {
"version": "2.2.1",
+ "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2091,11 +3194,15 @@
},
"node_modules/err-code": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"dev": true,
"license": "MIT"
},
"node_modules/es-define-property": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2104,6 +3211,8 @@
},
"node_modules/es-errors": {
"version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2112,6 +3221,8 @@
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2123,6 +3234,8 @@
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2137,12 +3250,55 @@
},
"node_modules/es6-error": {
"version": "4.1.1",
+ "resolved": "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true,
"license": "MIT",
"optional": true
},
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
"node_modules/escalade": {
"version": "3.2.0",
+ "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2151,6 +3307,8 @@
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -2163,11 +3321,15 @@
},
"node_modules/exponential-backoff": {
"version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
+ "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/extract-zip": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -2187,6 +3349,8 @@
},
"node_modules/extsprintf": {
"version": "1.4.1",
+ "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.4.1.tgz",
+ "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
"dev": true,
"engines": [
"node >=0.6.0"
@@ -2196,16 +3360,22 @@
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
"node_modules/fd-slicer": {
"version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2214,6 +3384,8 @@
},
"node_modules/filelist": {
"version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz",
+ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -2222,11 +3394,15 @@
},
"node_modules/filelist/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2235,6 +3411,8 @@
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.9",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2246,6 +3424,8 @@
},
"node_modules/foreground-child": {
"version": "3.3.1",
+ "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2261,6 +3441,8 @@
},
"node_modules/foreground-child/node_modules/signal-exit": {
"version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
@@ -2272,6 +3454,8 @@
},
"node_modules/form-data": {
"version": "4.0.5",
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2287,12 +3471,16 @@
},
"node_modules/fs-constants": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/fs-extra": {
"version": "8.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2306,6 +3494,8 @@
},
"node_modules/fs-minipass": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2317,11 +3507,30 @@
},
"node_modules/fs.realpath": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"license": "ISC"
},
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmmirror.com/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/function-bind": {
"version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -2330,6 +3539,9 @@
},
"node_modules/gauge": {
"version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz",
+ "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2348,6 +3560,8 @@
},
"node_modules/get-caller-file": {
"version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
@@ -2356,6 +3570,8 @@
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2379,6 +3595,8 @@
},
"node_modules/get-proto": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2391,6 +3609,8 @@
},
"node_modules/get-stream": {
"version": "5.2.0",
+ "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2405,6 +3625,9 @@
},
"node_modules/glob": {
"version": "7.2.3",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2424,11 +3647,15 @@
},
"node_modules/glob/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "1.1.13",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2438,6 +3665,8 @@
},
"node_modules/glob/node_modules/minimatch": {
"version": "3.1.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2449,6 +3678,8 @@
},
"node_modules/global-agent": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz",
+ "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
@@ -2466,6 +3697,8 @@
},
"node_modules/global-agent/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"optional": true,
@@ -2478,6 +3711,8 @@
},
"node_modules/globalthis": {
"version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -2494,6 +3729,8 @@
},
"node_modules/gopd": {
"version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2505,6 +3742,8 @@
},
"node_modules/got": {
"version": "11.8.6",
+ "resolved": "https://registry.npmmirror.com/got/-/got-11.8.6.tgz",
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2529,11 +3768,15 @@
},
"node_modules/graceful-fs": {
"version": "4.2.11",
+ "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/has-flag": {
"version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2542,6 +3785,8 @@
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -2554,6 +3799,8 @@
},
"node_modules/has-symbols": {
"version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2565,6 +3812,8 @@
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2579,11 +3828,15 @@
},
"node_modules/has-unicode": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
"dev": true,
"license": "ISC"
},
"node_modules/hasown": {
"version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2595,6 +3848,8 @@
},
"node_modules/hosted-git-info": {
"version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+ "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2606,11 +3861,15 @@
},
"node_modules/http-cache-semantics": {
"version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
+ "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2623,6 +3882,8 @@
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2635,6 +3896,8 @@
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2647,14 +3910,36 @@
},
"node_modules/humanize-ms": {
"version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.0.0"
}
},
+ "node_modules/iconv-corefoundation": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmmirror.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
+ "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "dependencies": {
+ "cli-truncate": "^2.1.0",
+ "node-addon-api": "^1.6.3"
+ },
+ "engines": {
+ "node": "^8.11.2 || >=10"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.6.3",
+ "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2666,6 +3951,8 @@
},
"node_modules/ieee754": {
"version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [
{
@@ -2685,6 +3972,8 @@
},
"node_modules/imurmurhash": {
"version": "0.1.4",
+ "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2693,6 +3982,8 @@
},
"node_modules/indent-string": {
"version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2701,11 +3992,16 @@
},
"node_modules/infer-owner": {
"version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
"dev": true,
"license": "ISC"
},
"node_modules/inflight": {
"version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2715,11 +4011,15 @@
},
"node_modules/inherits": {
"version": "2.0.4",
+ "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/ip-address": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2728,6 +4028,8 @@
},
"node_modules/is-ci": {
"version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz",
+ "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2739,6 +4041,8 @@
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2747,6 +4051,8 @@
},
"node_modules/is-interactive": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2755,11 +4061,15 @@
},
"node_modules/is-lambda": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz",
+ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/is-unicode-supported": {
"version": "0.1.0",
+ "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2771,12 +4081,16 @@
},
"node_modules/isarray": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/isbinaryfile": {
"version": "5.0.7",
+ "resolved": "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
+ "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2788,11 +4102,15 @@
},
"node_modules/isexe": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/jackspeak": {
"version": "3.4.3",
+ "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -2807,6 +4125,8 @@
},
"node_modules/jake": {
"version": "10.9.4",
+ "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz",
+ "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -2823,6 +4143,8 @@
},
"node_modules/js-yaml": {
"version": "4.1.1",
+ "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2834,22 +4156,30 @@
},
"node_modules/json-buffer": {
"version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true,
"license": "ISC",
"optional": true
},
"node_modules/json5": {
"version": "2.2.3",
+ "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -2861,6 +4191,8 @@
},
"node_modules/jsonfile": {
"version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
@@ -2869,6 +4201,8 @@
},
"node_modules/keyv": {
"version": "4.5.4",
+ "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2877,11 +4211,15 @@
},
"node_modules/lazy-val": {
"version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz",
+ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lazystream": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -2894,6 +4232,8 @@
},
"node_modules/lazystream/node_modules/readable-stream": {
"version": "2.3.8",
+ "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -2909,12 +4249,16 @@
},
"node_modules/lazystream/node_modules/safe-buffer": {
"version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lazystream/node_modules/string_decoder": {
"version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -2924,41 +4268,55 @@
},
"node_modules/lodash": {
"version": "4.18.1",
+ "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.difference": {
"version": "4.5.0",
+ "resolved": "https://registry.npmmirror.com/lodash.difference/-/lodash.difference-4.5.0.tgz",
+ "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.flatten": {
"version": "4.4.0",
+ "resolved": "https://registry.npmmirror.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
+ "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/lodash.union": {
"version": "4.6.0",
+ "resolved": "https://registry.npmmirror.com/lodash.union/-/lodash.union-4.6.0.tgz",
+ "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/log-symbols": {
"version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2974,6 +4332,8 @@
},
"node_modules/lowercase-keys": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2982,6 +4342,8 @@
},
"node_modules/lru-cache": {
"version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2993,6 +4355,8 @@
},
"node_modules/make-fetch-happen": {
"version": "10.2.1",
+ "resolved": "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
+ "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3019,6 +4383,8 @@
},
"node_modules/make-fetch-happen/node_modules/agent-base": {
"version": "6.0.2",
+ "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3030,6 +4396,8 @@
},
"node_modules/make-fetch-happen/node_modules/http-proxy-agent": {
"version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3043,6 +4411,8 @@
},
"node_modules/make-fetch-happen/node_modules/https-proxy-agent": {
"version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3055,6 +4425,8 @@
},
"node_modules/make-fetch-happen/node_modules/lru-cache": {
"version": "7.18.3",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -3063,6 +4435,8 @@
},
"node_modules/matcher": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz",
+ "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3075,6 +4449,8 @@
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3083,6 +4459,8 @@
},
"node_modules/mime": {
"version": "2.6.0",
+ "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -3094,6 +4472,8 @@
},
"node_modules/mime-db": {
"version": "1.52.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3102,6 +4482,8 @@
},
"node_modules/mime-types": {
"version": "2.1.35",
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3113,6 +4495,8 @@
},
"node_modules/mimic-fn": {
"version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3121,6 +4505,8 @@
},
"node_modules/mimic-response": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3129,6 +4515,8 @@
},
"node_modules/minimatch": {
"version": "10.2.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -3143,6 +4531,8 @@
},
"node_modules/minimist": {
"version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
@@ -3151,6 +4541,8 @@
},
"node_modules/minipass": {
"version": "3.3.6",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3162,6 +4554,8 @@
},
"node_modules/minipass-collect": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3173,6 +4567,8 @@
},
"node_modules/minipass-fetch": {
"version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
+ "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3189,6 +4585,8 @@
},
"node_modules/minipass-flush": {
"version": "1.0.7",
+ "resolved": "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.7.tgz",
+ "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -3200,6 +4598,8 @@
},
"node_modules/minipass-pipeline": {
"version": "1.2.4",
+ "resolved": "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3211,6 +4611,8 @@
},
"node_modules/minipass-sized": {
"version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz",
+ "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3222,6 +4624,8 @@
},
"node_modules/minizlib": {
"version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3234,6 +4638,8 @@
},
"node_modules/mkdirp": {
"version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -3245,11 +4651,34 @@
},
"node_modules/ms": {
"version": "2.1.3",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "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/negotiator": {
"version": "0.6.4",
+ "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz",
+ "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3258,6 +4687,8 @@
},
"node_modules/node-abi": {
"version": "3.89.0",
+ "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.89.0.tgz",
+ "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3269,6 +4700,8 @@
},
"node_modules/node-abi/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3280,12 +4713,16 @@
},
"node_modules/node-addon-api": {
"version": "1.7.2",
+ "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-1.7.2.tgz",
+ "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/node-api-version": {
"version": "0.2.1",
+ "resolved": "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.1.tgz",
+ "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3294,6 +4731,8 @@
},
"node_modules/node-api-version/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3305,6 +4744,8 @@
},
"node_modules/node-gyp": {
"version": "9.4.1",
+ "resolved": "https://registry.npmmirror.com/node-gyp/-/node-gyp-9.4.1.tgz",
+ "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3329,6 +4770,8 @@
},
"node_modules/node-gyp/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3340,6 +4783,8 @@
},
"node_modules/nopt": {
"version": "6.0.0",
+ "resolved": "https://registry.npmmirror.com/nopt/-/nopt-6.0.0.tgz",
+ "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3354,6 +4799,8 @@
},
"node_modules/normalize-path": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -3363,6 +4810,8 @@
},
"node_modules/normalize-url": {
"version": "6.1.0",
+ "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz",
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3374,6 +4823,9 @@
},
"node_modules/npmlog": {
"version": "6.0.2",
+ "resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-6.0.2.tgz",
+ "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3388,6 +4840,8 @@
},
"node_modules/object-keys": {
"version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3397,6 +4851,8 @@
},
"node_modules/once": {
"version": "1.4.0",
+ "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3405,6 +4861,8 @@
},
"node_modules/onetime": {
"version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3419,6 +4877,8 @@
},
"node_modules/ora": {
"version": "5.4.1",
+ "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3441,6 +4901,8 @@
},
"node_modules/p-cancelable": {
"version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz",
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3449,6 +4911,8 @@
},
"node_modules/p-limit": {
"version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3463,6 +4927,8 @@
},
"node_modules/p-map": {
"version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3477,11 +4943,15 @@
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3490,6 +4960,8 @@
},
"node_modules/path-key": {
"version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3498,6 +4970,8 @@
},
"node_modules/path-scurry": {
"version": "1.11.1",
+ "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -3513,11 +4987,15 @@
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.4.3",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"dev": true,
"license": "ISC"
},
"node_modules/path-scurry/node_modules/minipass": {
"version": "7.1.3",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
@@ -3526,6 +5004,8 @@
},
"node_modules/pe-library": {
"version": "0.4.1",
+ "resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz",
+ "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3539,16 +5019,22 @@
},
"node_modules/pend": {
"version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true,
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/plist": {
"version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.0.tgz",
+ "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3560,14 +5046,47 @@
"node": ">=10.4.0"
}
},
+ "node_modules/postcss": {
+ "version": "8.5.8",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz",
+ "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
+ "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.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/progress": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3576,11 +5095,15 @@
},
"node_modules/promise-inflight": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
"dev": true,
"license": "ISC"
},
"node_modules/promise-retry": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3593,6 +5116,8 @@
},
"node_modules/pump": {
"version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3602,6 +5127,8 @@
},
"node_modules/punycode": {
"version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3610,6 +5137,8 @@
},
"node_modules/quick-lru": {
"version": "5.1.1",
+ "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3621,6 +5150,8 @@
},
"node_modules/read-binary-file-arch": {
"version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz",
+ "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3632,6 +5163,8 @@
},
"node_modules/readable-stream": {
"version": "3.6.2",
+ "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3645,6 +5178,8 @@
},
"node_modules/readdir-glob": {
"version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
@@ -3654,12 +5189,16 @@
},
"node_modules/readdir-glob/node_modules/balanced-match": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/readdir-glob/node_modules/brace-expansion": {
"version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.3.tgz",
+ "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -3669,6 +5208,8 @@
},
"node_modules/readdir-glob/node_modules/minimatch": {
"version": "5.1.9",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"peer": true,
@@ -3681,6 +5222,8 @@
},
"node_modules/require-directory": {
"version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3689,6 +5232,8 @@
},
"node_modules/resedit": {
"version": "1.7.2",
+ "resolved": "https://registry.npmmirror.com/resedit/-/resedit-1.7.2.tgz",
+ "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3705,11 +5250,15 @@
},
"node_modules/resolve-alpn": {
"version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
"dev": true,
"license": "MIT"
},
"node_modules/responselike": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz",
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3721,6 +5270,8 @@
},
"node_modules/restore-cursor": {
"version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3733,6 +5284,8 @@
},
"node_modules/retry": {
"version": "0.12.0",
+ "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3741,6 +5294,9 @@
},
"node_modules/rimraf": {
"version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3755,6 +5311,8 @@
},
"node_modules/roarr": {
"version": "2.15.4",
+ "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz",
+ "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
@@ -3770,8 +5328,55 @@
"node": ">=8.0"
}
},
+ "node_modules/rollup": {
+ "version": "4.60.1",
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz",
+ "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.60.1",
+ "@rollup/rollup-android-arm64": "4.60.1",
+ "@rollup/rollup-darwin-arm64": "4.60.1",
+ "@rollup/rollup-darwin-x64": "4.60.1",
+ "@rollup/rollup-freebsd-arm64": "4.60.1",
+ "@rollup/rollup-freebsd-x64": "4.60.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.60.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.60.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.60.1",
+ "@rollup/rollup-linux-arm64-musl": "4.60.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.60.1",
+ "@rollup/rollup-linux-loong64-musl": "4.60.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.60.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.60.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.60.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.60.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.60.1",
+ "@rollup/rollup-linux-x64-gnu": "4.60.1",
+ "@rollup/rollup-linux-x64-musl": "4.60.1",
+ "@rollup/rollup-openbsd-x64": "4.60.1",
+ "@rollup/rollup-openharmony-arm64": "4.60.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.60.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.60.1",
+ "@rollup/rollup-win32-x64-gnu": "4.60.1",
+ "@rollup/rollup-win32-x64-msvc": "4.60.1",
+ "fsevents": "~2.3.2"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
+ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
@@ -3791,11 +5396,15 @@
},
"node_modules/safer-buffer": {
"version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/sanitize-filename": {
"version": "1.6.4",
+ "resolved": "https://registry.npmmirror.com/sanitize-filename/-/sanitize-filename-1.6.4.tgz",
+ "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==",
"dev": true,
"license": "WTFPL OR ISC",
"dependencies": {
@@ -3804,6 +5413,8 @@
},
"node_modules/sax": {
"version": "1.6.0",
+ "resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz",
+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
@@ -3812,6 +5423,8 @@
},
"node_modules/semver": {
"version": "6.3.1",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3820,12 +5433,16 @@
},
"node_modules/semver-compare": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz",
+ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/serialize-error": {
"version": "7.0.1",
+ "resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz",
+ "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3841,11 +5458,15 @@
},
"node_modules/set-blocking": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true,
"license": "ISC"
},
"node_modules/shebang-command": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3857,6 +5478,8 @@
},
"node_modules/shebang-regex": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3865,11 +5488,15 @@
},
"node_modules/signal-exit": {
"version": "3.0.7",
+ "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true,
"license": "ISC"
},
"node_modules/simple-update-notifier": {
"version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3881,6 +5508,8 @@
},
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.7.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
@@ -3892,6 +5521,8 @@
},
"node_modules/slice-ansi": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz",
+ "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3906,6 +5537,8 @@
},
"node_modules/smart-buffer": {
"version": "4.2.0",
+ "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3915,6 +5548,8 @@
},
"node_modules/socks": {
"version": "2.8.7",
+ "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.7.tgz",
+ "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3928,6 +5563,8 @@
},
"node_modules/socks-proxy-agent": {
"version": "7.0.0",
+ "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+ "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3941,6 +5578,8 @@
},
"node_modules/socks-proxy-agent/node_modules/agent-base": {
"version": "6.0.2",
+ "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3952,6 +5591,18 @@
},
"node_modules/source-map": {
"version": "0.6.1",
+ "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -3960,6 +5611,8 @@
},
"node_modules/source-map-support": {
"version": "0.5.21",
+ "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3969,12 +5622,16 @@
},
"node_modules/sprintf-js": {
"version": "1.1.3",
+ "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true
},
"node_modules/ssri": {
"version": "9.0.1",
+ "resolved": "https://registry.npmmirror.com/ssri/-/ssri-9.0.1.tgz",
+ "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -3986,6 +5643,8 @@
},
"node_modules/stat-mode": {
"version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/stat-mode/-/stat-mode-1.0.0.tgz",
+ "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3994,6 +5653,8 @@
},
"node_modules/string_decoder": {
"version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4002,6 +5663,8 @@
},
"node_modules/string-width": {
"version": "4.2.3",
+ "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4016,6 +5679,8 @@
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
+ "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4029,6 +5694,8 @@
},
"node_modules/strip-ansi": {
"version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4041,6 +5708,8 @@
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4052,6 +5721,8 @@
},
"node_modules/sumchecker": {
"version": "3.0.1",
+ "resolved": "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz",
+ "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -4063,6 +5734,8 @@
},
"node_modules/supports-color": {
"version": "7.2.0",
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4074,6 +5747,8 @@
},
"node_modules/tar": {
"version": "6.2.1",
+ "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4090,6 +5765,8 @@
},
"node_modules/tar-stream": {
"version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -4106,6 +5783,8 @@
},
"node_modules/tar/node_modules/minipass": {
"version": "5.0.0",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"dev": true,
"license": "ISC",
"engines": {
@@ -4114,6 +5793,8 @@
},
"node_modules/temp-file": {
"version": "3.4.0",
+ "resolved": "https://registry.npmmirror.com/temp-file/-/temp-file-3.4.0.tgz",
+ "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4123,6 +5804,8 @@
},
"node_modules/temp-file/node_modules/fs-extra": {
"version": "10.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4136,6 +5819,8 @@
},
"node_modules/temp-file/node_modules/jsonfile": {
"version": "6.2.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4147,6 +5832,8 @@
},
"node_modules/temp-file/node_modules/universalify": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4155,6 +5842,8 @@
},
"node_modules/tmp": {
"version": "0.2.5",
+ "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4163,6 +5852,8 @@
},
"node_modules/tmp-promise": {
"version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz",
+ "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4171,6 +5862,8 @@
},
"node_modules/truncate-utf8-bytes": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
+ "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
"dev": true,
"license": "WTFPL",
"dependencies": {
@@ -4179,6 +5872,8 @@
},
"node_modules/type-fest": {
"version": "0.13.1",
+ "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"optional": true,
@@ -4191,6 +5886,8 @@
},
"node_modules/typescript": {
"version": "5.9.3",
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4203,11 +5900,15 @@
},
"node_modules/undici-types": {
"version": "6.21.0",
+ "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/unique-filename": {
"version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/unique-filename/-/unique-filename-2.0.1.tgz",
+ "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4219,6 +5920,8 @@
},
"node_modules/unique-slug": {
"version": "3.0.0",
+ "resolved": "https://registry.npmmirror.com/unique-slug/-/unique-slug-3.0.0.tgz",
+ "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4230,6 +5933,8 @@
},
"node_modules/universalify": {
"version": "0.1.2",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4238,6 +5943,8 @@
},
"node_modules/uri-js": {
"version": "4.4.1",
+ "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -4246,16 +5953,22 @@
},
"node_modules/utf8-byte-length": {
"version": "1.0.5",
+ "resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
+ "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
"dev": true,
"license": "(WTFPL OR MIT)"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"license": "MIT"
},
"node_modules/verror": {
"version": "1.10.1",
+ "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.1.tgz",
+ "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -4268,8 +5981,70 @@
"node": ">=0.6.0"
}
},
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.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",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
"node_modules/wcwidth": {
"version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4278,6 +6053,8 @@
},
"node_modules/which": {
"version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4292,6 +6069,8 @@
},
"node_modules/wide-align": {
"version": "1.1.5",
+ "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -4300,6 +6079,8 @@
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
+ "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4317,6 +6098,8 @@
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
+ "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4333,11 +6116,15 @@
},
"node_modules/wrappy": {
"version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
},
"node_modules/xmlbuilder": {
"version": "15.1.1",
+ "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
+ "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4346,6 +6133,8 @@
},
"node_modules/y18n": {
"version": "5.0.8",
+ "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -4354,11 +6143,15 @@
},
"node_modules/yallist": {
"version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true,
"license": "ISC"
},
"node_modules/yargs": {
"version": "17.7.2",
+ "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4376,6 +6169,8 @@
},
"node_modules/yargs-parser": {
"version": "21.1.1",
+ "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
@@ -4384,6 +6179,8 @@
},
"node_modules/yauzl": {
"version": "2.10.0",
+ "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4393,6 +6190,8 @@
},
"node_modules/yocto-queue": {
"version": "0.1.0",
+ "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4404,6 +6203,8 @@
},
"node_modules/zip-stream": {
"version": "4.1.1",
+ "resolved": "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.1.tgz",
+ "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -4418,6 +6219,8 @@
},
"node_modules/zip-stream/node_modules/archiver-utils": {
"version": "3.0.4",
+ "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-3.0.4.tgz",
+ "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
"dev": true,
"license": "MIT",
"peer": true,
diff --git a/package.json b/package.json
index 2443a32..95aedce 100644
--- a/package.json
+++ b/package.json
@@ -6,13 +6,16 @@
"author": "thzxx",
"license": "MIT",
"scripts": {
- "dev": "vite && electron .",
- "build": "tsc && vite build",
- "start": "electron . --no-sandbox",
- "pack": "electron-builder --dir",
- "dist": "electron-builder --win",
- "dist:nsis": "electron-builder --win nsis",
- "dist:portable": "electron-builder --win portable"
+ "dev:renderer": "vite build --watch",
+ "dev:main": "tsc -p tsconfig.main.json --watch",
+ "build:renderer": "vite build",
+ "build:main": "tsc -p tsconfig.main.json",
+ "build": "npm run build:renderer && npm run build:main",
+ "start": "npm run build && electron . --no-sandbox",
+ "pack": "npm run build && electron-builder --dir",
+ "dist": "npm run build && electron-builder --win",
+ "dist:nsis": "npm run build && electron-builder --win nsis",
+ "dist:portable": "npm run build && electron-builder --win portable"
},
"build": {
"appId": "com.metona.ollama-desktop",
diff --git a/src/main/main.ts b/src/main/main.ts
index 8b5460a..3e11c9a 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -18,6 +18,10 @@ const IS_DEV = !app.isPackaged;
export let mainWindow: BrowserWindow | null = null;
export let isQuitting = false;
+export function setQuitting(): void {
+ isQuitting = true;
+}
+
export function getIconPath(): string {
return process.platform === 'win32' ? ICO_PATH : ICON_PATH;
}
@@ -45,7 +49,6 @@ function createMainWindow(): BrowserWindow {
backgroundColor: '#202020',
show: false,
autoHideMenuBar: true,
- menuBarVisible: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
@@ -56,7 +59,13 @@ function createMainWindow(): BrowserWindow {
}
});
- mainWindow.loadFile(path.join(__dirname, '..', '..', 'index.html'));
+ // 开发模式:从 src/renderer/ 加载;生产模式:从 dist/renderer/ 加载
+ const isDev = !app.isPackaged;
+ if (isDev) {
+ mainWindow.loadFile(path.join(__dirname, '..', '..', 'src', 'renderer', 'index.html'));
+ } else {
+ mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'index.html'));
+ }
mainWindow.setMenuBarVisibility(false);
mainWindow.once('ready-to-show', () => {
diff --git a/src/main/menu.ts b/src/main/menu.ts
index 6d59670..4c7a7fe 100644
--- a/src/main/menu.ts
+++ b/src/main/menu.ts
@@ -2,8 +2,8 @@
* Metona Ollama Desktop - 原生菜单
*/
-import { Menu, dialog, shell } from 'electron';
-import { mainWindow, isQuitting, getIconPath } from './main.js';
+import { Menu, dialog, shell, app } from 'electron';
+import { mainWindow, setQuitting, getIconPath } from './main.js';
export function createMenu(): void {
const template: Electron.MenuItemConstructorOptions[] = [
@@ -30,8 +30,7 @@ export function createMenu(): void {
label: '退出',
accelerator: 'Ctrl+Q',
click: () => {
- const { app } = require('electron');
- (require('./main.js') as Record).isQuitting = true;
+ setQuitting();
app.quit();
}
}
@@ -112,8 +111,7 @@ export function createMenu(): void {
}
];
- const IS_DEV = !require('electron').app.isPackaged;
- if (IS_DEV) {
+ if (!app.isPackaged) {
template.push({
label: 'Dev',
submenu: [
diff --git a/src/main/tray.ts b/src/main/tray.ts
index a7d911f..7046357 100644
--- a/src/main/tray.ts
+++ b/src/main/tray.ts
@@ -2,8 +2,8 @@
* Metona Ollama Desktop - 系统托盘
*/
-import { Tray, Menu, nativeImage } from 'electron';
-import { mainWindow, isQuitting, getIconPath } from './main.js';
+import { Tray, Menu, nativeImage, app } from 'electron';
+import { mainWindow, setQuitting, getIconPath } from './main.js';
let tray: Tray | null = null;
@@ -32,9 +32,8 @@ export function createTray(): void {
{
label: '退出',
click: () => {
- const main = require('./main.js');
- main.isQuitting = true;
- require('electron').app.quit();
+ setQuitting();
+ app.quit();
}
}
]);
diff --git a/src/renderer/index.html b/src/renderer/index.html
index 9e1ef0d..d6af2ab 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -5,7 +5,7 @@
-
+
Metona Ollama
diff --git a/src/renderer/main.ts b/src/renderer/main.ts
index 7adcfb8..bec3818 100644
--- a/src/renderer/main.ts
+++ b/src/renderer/main.ts
@@ -3,8 +3,7 @@
* 负责初始化所有组件、协调事件流、管理生命周期
*/
-import '../styles/style.css';
-import './types.js';
+import './styles/style.css';
import { ChatDB } from './db/chat-db.js';
import { OllamaAPI } from './api/ollama.js';
diff --git a/sw.js b/sw.js
deleted file mode 100644
index 67b4fab..0000000
--- a/sw.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Service Worker - PWA 离线支持
- * 缓存应用资源以便离线使用
- */
-
-const CACHE_NAME = 'metona-ollama-v3';
-const ASSETS = [
- './',
- './index.html',
- './css/style.css',
- './js/chat-db.js',
- './js/lib/marked.esm.js',
- './js/ollama-api.js',
- './manifest.json'
-];
-
-// 安装阶段:预缓存核心资源
-self.addEventListener('install', (event) => {
- event.waitUntil(
- caches.open(CACHE_NAME)
- .then(cache => cache.addAll(ASSETS))
- .then(() => self.skipWaiting())
- );
-});
-
-// 激活阶段:清理旧缓存
-self.addEventListener('activate', (event) => {
- event.waitUntil(
- caches.keys()
- .then(keys => Promise.all(
- keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key))
- ))
- .then(() => self.clients.claim())
- );
-});
-
-// 拦截请求:缓存优先,网络回退
-self.addEventListener('fetch', (event) => {
- // 不拦截非 GET 请求
- if (event.request.method !== 'GET') return;
-
- event.respondWith(
- caches.match(event.request)
- .then(cached => {
- if (cached) return cached;
- return fetch(event.request).then(response => {
- // 仅缓存成功的响应
- if (response && response.status === 200) {
- const clone = response.clone();
- caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
- }
- return response;
- }).catch(() => {
- // 离线且无缓存时返回简单提示
- if (event.request.destination === 'document') {
- return new Response('离线模式:请检查网络连接', {
- headers: { 'Content-Type': 'text/plain; charset=utf-8' }
- });
- }
- });
- })
- );
-});
diff --git a/tsconfig.json b/tsconfig.json
index 86464f5..a0b9de1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -13,18 +13,11 @@
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
- "declaration": true,
- "declarationMap": true,
"sourceMap": true,
- "outDir": "dist",
- "rootDir": "src",
- "baseUrl": ".",
- "paths": {
- "@main/*": ["src/main/*"],
- "@renderer/*": ["src/renderer/*"]
- },
+ "outDir": "dist/renderer",
+ "rootDir": "src/renderer",
"lib": ["ES2022", "DOM", "DOM.Iterable"]
},
- "include": ["src/**/*.ts", "src/**/*.d.ts"],
+ "include": ["src/renderer/**/*.ts", "src/renderer/**/*.d.ts"],
"exclude": ["node_modules", "dist", "release"]
}
diff --git a/tsconfig.main.json b/tsconfig.main.json
new file mode 100644
index 0000000..e4b2200
--- /dev/null
+++ b/tsconfig.main.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "CommonJS",
+ "moduleResolution": "node",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "outDir": "dist/main",
+ "rootDir": "src/main",
+ "sourceMap": true,
+ "declaration": false
+ },
+ "include": ["src/main/**/*.ts"],
+ "exclude": ["node_modules", "dist", "release"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..1a1f803
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,20 @@
+import { defineConfig } from 'vite';
+import { resolve } from 'path';
+
+export default defineConfig({
+ root: 'src/renderer',
+ base: './',
+ build: {
+ outDir: '../../dist/renderer',
+ emptyOutDir: true,
+ rollupOptions: {
+ input: resolve(__dirname, 'src/renderer/index.html'),
+ },
+ },
+ css: {
+ preprocessorOptions: {},
+ },
+ server: {
+ port: 5173,
+ },
+});