🎨 优化扩展模块,完成ai接入和对话功能
This commit is contained in:
299
docs/demo.html
Normal file
299
docs/demo.html
Normal file
@@ -0,0 +1,299 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
/* --- 沉浸式怪谈配色方案 --- */
|
||||
:root {
|
||||
--paper-bg: #e8e4d8; /* 陈旧纸张色 */
|
||||
--ink-black: #1a1a1a; /* 墨色 */
|
||||
--ghost-fire: #78ffc6; /* 磷火青 */
|
||||
--seal-red: #8f2626; /* 朱砂红 */
|
||||
--seal-red-glow: #ff4d4d; /* 鲜血红光 */
|
||||
--font-main: "SimSun", "Songti SC", "Noto Serif SC", serif;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0; padding: 0;
|
||||
background: transparent;
|
||||
font-family: var(--font-main);
|
||||
color: var(--paper-bg);
|
||||
display: flex; justify-content: center; align-items: center;
|
||||
overflow: hidden;
|
||||
user-select: none; /* 防止划动时选中文本 */
|
||||
}
|
||||
|
||||
/* --- 主容器:灵视窗口 --- */
|
||||
.occult-frame {
|
||||
position: relative;
|
||||
width: 100%; max-width: 520px;
|
||||
background: linear-gradient(180deg, #0f1012 0%, #050505 100%);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,0.9), 0 0 0 1px #333;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 顶部装饰:注连绳/结界风格 */
|
||||
.frame-header {
|
||||
height: 6px;
|
||||
background: repeating-linear-gradient(
|
||||
45deg,
|
||||
var(--ink-black),
|
||||
var(--ink-black) 10px,
|
||||
var(--seal-red) 10px,
|
||||
var(--seal-red) 20px
|
||||
);
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
/* --- 滑动轨道 --- */
|
||||
.slider-window {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.slider-track {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
transition: transform 0.5s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
/* --- 卡片通用样式 --- */
|
||||
.scenario-card {
|
||||
flex: 0 0 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 30px 25px 40px 25px;
|
||||
position: relative;
|
||||
opacity: 0.3;
|
||||
transform: scale(0.96);
|
||||
transition: all 0.5s ease;
|
||||
filter: grayscale(80%);
|
||||
}
|
||||
|
||||
.scenario-card.active {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
filter: grayscale(0%);
|
||||
}
|
||||
|
||||
/* 背景装饰字 */
|
||||
.bg-kanji {
|
||||
position: absolute; top: 10px; right: 10px;
|
||||
font-size: 120px; font-weight: bold;
|
||||
opacity: 0.03; line-height: 1;
|
||||
pointer-events: none; z-index: 0;
|
||||
font-family: "SimSun", serif;
|
||||
}
|
||||
|
||||
/* 标题组 */
|
||||
.title-group {
|
||||
position: relative; z-index: 2;
|
||||
border-left: 3px solid var(--ghost-fire);
|
||||
padding-left: 15px; margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 12px; letter-spacing: 2px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 32px; font-weight: bold;
|
||||
margin-top: 4px;
|
||||
text-shadow: 0 2px 10px rgba(0,0,0,0.8);
|
||||
letter-spacing: 3px;
|
||||
}
|
||||
|
||||
/* 正文区域 */
|
||||
.context-box {
|
||||
position: relative; z-index: 2;
|
||||
font-size: 14px; line-height: 1.8;
|
||||
color: #ccc;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
padding: 15px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 引导提示 */
|
||||
.guide-box {
|
||||
margin-top: 20px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
/* --- 风格差异化 --- */
|
||||
/* 场景一:日常/入部 */
|
||||
.scenario-card.intro .main-title { color: var(--paper-bg); }
|
||||
.scenario-card.intro .title-group { border-color: #aaa; }
|
||||
|
||||
/* 场景二:怪谈/深渊 */
|
||||
.scenario-card.horror .main-title {
|
||||
color: var(--seal-red);
|
||||
text-shadow: 0 0 10px rgba(143, 38, 38, 0.6);
|
||||
}
|
||||
.scenario-card.horror .title-group { border-color: var(--seal-red); }
|
||||
.scenario-card.horror .bg-kanji { color: var(--seal-red-glow); opacity: 0.08; }
|
||||
|
||||
.scenario-card.horror .context-box {
|
||||
border-color: rgba(143, 38, 38, 0.3);
|
||||
background: linear-gradient(180deg, rgba(20,0,0,0.4) 0%, rgba(40,10,10,0.6) 100%);
|
||||
}
|
||||
|
||||
/* --- 底部导航 --- */
|
||||
.nav-indicator {
|
||||
display: flex; justify-content: center; gap: 8px;
|
||||
padding-bottom: 15px;
|
||||
position: relative; z-index: 10;
|
||||
}
|
||||
.dot {
|
||||
width: 40px; height: 3px; background: #333;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.dot.active { background: var(--ghost-fire); box-shadow: 0 0 5px var(--ghost-fire); }
|
||||
.dot.active-red { background: var(--seal-red-glow); box-shadow: 0 0 5px var(--seal-red-glow); }
|
||||
|
||||
/* 呼吸动画 */
|
||||
@keyframes breathe { 0%,100%{opacity:0.6;} 50%{opacity:1;} }
|
||||
.guide-blink { animation: breathe 3s infinite ease-in-out; }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="occult-frame">
|
||||
<div class="frame-header"></div>
|
||||
|
||||
<div class="slider-window">
|
||||
<div class="slider-track" id="track">
|
||||
|
||||
<!-- 场景一:初次访问 (左) -->
|
||||
<div class="scenario-card intro active">
|
||||
<div class="bg-kanji">日常</div>
|
||||
<div class="title-group">
|
||||
<div class="sub-title">SCENARIO_01</div>
|
||||
<div class="main-title">旧校舍的访客</div>
|
||||
</div>
|
||||
<div class="context-box">
|
||||
<p>热闹非凡的社团招新日。躲避喧闹的你在老槐树下见到了奇特的少女。</p>
|
||||
<p>印着“怪谈社”三个字的手绘传单递到手中时,命运的齿轮已经开始转动。</p>
|
||||
</div>
|
||||
<div class="guide-box guide-blink">
|
||||
>>> 适合初次体验剧情的“新人部员” <<< <br>
|
||||
请手动输入以此情境为开头的行动。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 场景二:深渊重临 (右) -->
|
||||
<div class="scenario-card horror">
|
||||
<div class="bg-kanji">怪谈</div>
|
||||
<div class="title-group">
|
||||
<div class="sub-title">SCENARIO_02</div>
|
||||
<div class="main-title">百物语之夜</div>
|
||||
</div>
|
||||
<div class="context-box">
|
||||
<p>不需要多余的寒暄。青行灯的火光在黑暗中幽微地摇曳。</p>
|
||||
<p>作为已经缔结契约的共犯,你熟练地拉开椅子。白川绮罗香正等待着你,准备开启下一个怪谈的封印。</p>
|
||||
</div>
|
||||
<div class="guide-box guide-blink">
|
||||
>>> 适合跳过引导的“资深调查员” <<< <br>
|
||||
请直接询问“今天的怪谈是什么?”
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav-indicator">
|
||||
<div class="dot active" id="dot0"></div>
|
||||
<div class="dot" id="dot1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const track = document.getElementById('track');
|
||||
const cards = document.querySelectorAll('.scenario-card');
|
||||
const dots = [document.getElementById('dot0'), document.getElementById('dot1')];
|
||||
let currentIndex = 0;
|
||||
|
||||
// 更新视图状态
|
||||
function updateSlide(index) {
|
||||
currentIndex = index;
|
||||
// 移动轨道
|
||||
track.style.transform = `translateX(-${index * 100}%)`;
|
||||
|
||||
// 卡片激活状态
|
||||
cards.forEach((c, i) => {
|
||||
c.classList.toggle('active', i === index);
|
||||
});
|
||||
|
||||
// 底部指示条
|
||||
dots.forEach((d, i) => {
|
||||
d.className = 'dot'; // 重置
|
||||
if (i === index) {
|
||||
// 如果是第二页(index 1),用红色样式
|
||||
d.classList.add(index === 1 ? 'active-red' : 'active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- 触摸/鼠标滑动逻辑 ---
|
||||
let startX = 0;
|
||||
let isDragging = false;
|
||||
|
||||
// 触摸事件
|
||||
track.addEventListener('touchstart', e => {
|
||||
startX = e.touches[0].clientX;
|
||||
isDragging = true;
|
||||
}, {passive: true});
|
||||
|
||||
track.addEventListener('touchend', e => {
|
||||
if (!isDragging) return;
|
||||
handleSwipe(e.changedTouches[0].clientX);
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// 鼠标事件 (兼容PC)
|
||||
track.addEventListener('mousedown', e => {
|
||||
startX = e.clientX;
|
||||
isDragging = true;
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', e => {
|
||||
if (!isDragging) return;
|
||||
handleSwipe(e.clientX);
|
||||
isDragging = false;
|
||||
});
|
||||
|
||||
// 处理滑动方向
|
||||
function handleSwipe(endX) {
|
||||
const diff = endX - startX;
|
||||
const threshold = 50; // 滑动阈值
|
||||
|
||||
if (Math.abs(diff) > threshold) {
|
||||
if (diff > 0 && currentIndex > 0) {
|
||||
// 向右滑 -> 上一页
|
||||
updateSlide(0);
|
||||
} else if (diff < 0 && currentIndex < 1) {
|
||||
// 向左滑 -> 下一页
|
||||
updateSlide(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击指示器切换
|
||||
dots.forEach((dot, index) => {
|
||||
dot.addEventListener('click', () => updateSlide(index));
|
||||
});
|
||||
|
||||
// 初始化
|
||||
updateSlide(0);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
228
docs/扩展模块修复说明.md
228
docs/扩展模块修复说明.md
@@ -1,228 +0,0 @@
|
||||
# 扩展模块修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
1. **数据库错误**:点击扩展配置时报错 `ERROR: relation "ai_extension_settings" does not exist`
|
||||
2. **UI 不符合预期**:原来是表格式,用户希望是折叠面板式(类似原版 SillyTavern)
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 简化数据库设计
|
||||
|
||||
**问题**:原设计使用独立的 `ai_extension_settings` 表存储用户配置,导致表不存在时报错。
|
||||
|
||||
**解决方案**:取消 `ai_extension_settings` 表,直接将配置存储在 `ai_extensions` 表的 `settings` 字段中。
|
||||
|
||||
**修改文件**:
|
||||
- `server/model/app/ai_extension.go` - 注释掉 `AIExtensionSettings` 结构体
|
||||
- `server/initialize/gorm.go` - 移除 `AIExtensionSettings` 表注册
|
||||
- `server/service/app/extension.go` - 简化配置获取和更新逻辑
|
||||
|
||||
### 2. 重新设计 UI 为折叠面板式
|
||||
|
||||
**新界面特点**:
|
||||
- ✅ 折叠面板布局(类似原版 SillyTavern)
|
||||
- ✅ 每个扩展可展开查看详情和配置
|
||||
- ✅ 启用/禁用开关在标题栏
|
||||
- ✅ 配置项根据 manifest.json 动态生成
|
||||
- ✅ 支持多种配置类型:
|
||||
- 文本输入(text/string)
|
||||
- 数字输入(number)
|
||||
- 布尔开关(boolean/checkbox)
|
||||
- 下拉选择(select)
|
||||
- 文本域(textarea)
|
||||
- 滑块(slider)
|
||||
|
||||
**新文件**:
|
||||
- `web-app-vue/src/views/extension/ExtensionListNew.vue` - 全新的折叠面板式界面
|
||||
|
||||
**修改文件**:
|
||||
- `web-app-vue/src/router/index.ts` - 更新路由指向新界面
|
||||
|
||||
### 3. 优化配置处理逻辑
|
||||
|
||||
**后端改进**:
|
||||
|
||||
```go
|
||||
// GetExtensionSettings - 改进版
|
||||
func (es *ExtensionService) GetExtensionSettings(userID, extensionID uint) (map[string]interface{}, error) {
|
||||
// 1. 验证扩展存在
|
||||
// 2. 返回扩展的 settings 字段
|
||||
// 3. 如果为空,从 manifestData 中提取默认配置
|
||||
// 4. 确保总是返回有效的配置对象(至少是空对象)
|
||||
}
|
||||
|
||||
// UpdateExtensionSettings - 简化版
|
||||
func (es *ExtensionService) UpdateExtensionSettings(userID, extensionID uint, settings map[string]interface{}) error {
|
||||
// 直接更新扩展表的 settings 字段
|
||||
}
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 安装扩展
|
||||
|
||||
支持三种安装方式:
|
||||
|
||||
1. **从文件导入**:上传 `manifest.json` 文件
|
||||
2. **从 Git 安装**:输入 Git 仓库 URL(支持 GitHub、GitLab、Gitee 等)
|
||||
3. **从 URL 安装**:直接输入 `manifest.json` 文件的 URL
|
||||
|
||||
### 管理扩展
|
||||
|
||||
1. **启用/禁用**:点击扩展标题栏的开关
|
||||
2. **查看详情**:点击扩展面板展开
|
||||
3. **配置扩展**:展开后直接编辑配置项,自动保存
|
||||
4. **导出扩展**:点击"导出"按钮下载 `manifest.json`
|
||||
5. **卸载扩展**:点击删除按钮(系统扩展不可卸载)
|
||||
|
||||
### 配置项说明
|
||||
|
||||
扩展的配置项根据 `manifest.json` 中的 `settings` 定义自动生成。例如:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "example-extension",
|
||||
"settings": {
|
||||
"apiKey": {
|
||||
"type": "text",
|
||||
"label": "API 密钥",
|
||||
"description": "请输入您的 API 密钥",
|
||||
"placeholder": "sk-..."
|
||||
},
|
||||
"maxRetries": {
|
||||
"type": "number",
|
||||
"label": "最大重试次数",
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"label": "启用此功能"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## manifest.json 配置项类型
|
||||
|
||||
支持的配置项类型:
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `text` / `string` | 文本输入框 | API 密钥、URL 等 |
|
||||
| `number` | 数字输入框 | 重试次数、超时时间等 |
|
||||
| `boolean` / `checkbox` | 开关 | 启用/禁用功能 |
|
||||
| `select` | 下拉选择 | 选择模型、语言等 |
|
||||
| `textarea` | 多行文本 | 提示词、说明文字等 |
|
||||
| `slider` | 滑块 | 温度、概率等 |
|
||||
|
||||
## 测试步骤
|
||||
|
||||
1. **重启后端服务**:确保新的代码生效
|
||||
2. **访问扩展管理页面**:`/extension`
|
||||
3. **安装一个测试扩展**:
|
||||
- 创建一个简单的 `manifest.json`
|
||||
- 从文件导入或 Git URL 安装
|
||||
4. **测试配置功能**:
|
||||
- 点击扩展面板展开
|
||||
- 修改配置项
|
||||
- 刷新页面确认配置已保存
|
||||
5. **测试启用/禁用**:
|
||||
- 点击开关切换状态
|
||||
- 确认状态已保存
|
||||
|
||||
## 示例 manifest.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "example-extension",
|
||||
"display_name": "示例扩展",
|
||||
"version": "1.0.0",
|
||||
"description": "这是一个示例扩展",
|
||||
"author": "Your Name",
|
||||
"type": "ui",
|
||||
"category": "utilities",
|
||||
"settings": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"label": "启用扩展",
|
||||
"description": "是否启用此扩展的功能",
|
||||
"default": true
|
||||
},
|
||||
"apiEndpoint": {
|
||||
"type": "text",
|
||||
"label": "API 端点",
|
||||
"description": "API 服务器地址",
|
||||
"placeholder": "https://api.example.com"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"label": "超时时间(秒)",
|
||||
"min": 1,
|
||||
"max": 300,
|
||||
"step": 1,
|
||||
"default": 30
|
||||
},
|
||||
"model": {
|
||||
"type": "select",
|
||||
"label": "选择模型",
|
||||
"options": [
|
||||
{ "label": "GPT-4", "value": "gpt-4" },
|
||||
{ "label": "GPT-3.5", "value": "gpt-3.5-turbo" }
|
||||
],
|
||||
"default": "gpt-3.5-turbo"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "textarea",
|
||||
"label": "系统提示词",
|
||||
"rows": 5,
|
||||
"placeholder": "输入系统提示词..."
|
||||
},
|
||||
"temperature": {
|
||||
"type": "slider",
|
||||
"label": "温度",
|
||||
"min": 0,
|
||||
"max": 2,
|
||||
"step": 0.1,
|
||||
"default": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 技术细节
|
||||
|
||||
### 数据库字段
|
||||
|
||||
扩展配置存储在 `ai_extensions` 表的以下字段:
|
||||
|
||||
- `manifest_data` (JSONB):完整的 `manifest.json` 内容,包含 `settings` 定义
|
||||
- `settings` (JSONB):用户的实际配置值
|
||||
|
||||
### 配置加载顺序
|
||||
|
||||
1. 从 `ai_extensions.settings` 读取用户配置
|
||||
2. 如果为空,从 `ai_extensions.manifest_data.settings` 提取默认值
|
||||
3. 合并配置,用户配置优先
|
||||
|
||||
### 配置保存
|
||||
|
||||
用户修改配置后,直接更新 `ai_extensions.settings` 字段。
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **配置格式**:`manifest.json` 中的 `settings` 字段是配置项的**定义**(schema),而不是配置值
|
||||
2. **默认值**:可以在配置项定义中指定 `default` 字段
|
||||
3. **验证**:前端会根据类型自动验证(如 `min`、`max`、`step` 等)
|
||||
4. **自动保存**:修改配置后会自动保存,无需手动点击保存按钮
|
||||
|
||||
## 未来改进
|
||||
|
||||
- [ ] 支持更多配置项类型(颜色选择器、文件选择器等)
|
||||
- [ ] 配置项分组和标签页
|
||||
- [ ] 配置导入/导出
|
||||
- [ ] 配置重置到默认值
|
||||
- [ ] 配置历史记录
|
||||
- [ ] 扩展依赖检查和冲突提示
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"name": "example-extension",
|
||||
"display_name": "示例扩展",
|
||||
"version": "1.0.0",
|
||||
"description": "这是一个用于测试的示例扩展,展示了各种配置项类型",
|
||||
"author": "测试作者",
|
||||
"homepage": "https://github.com/example/extension",
|
||||
"repository": "https://github.com/example/extension",
|
||||
"license": "MIT",
|
||||
"type": "ui",
|
||||
"category": "utilities",
|
||||
"tags": ["测试", "示例", "配置"],
|
||||
"settings": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"label": "启用扩展",
|
||||
"description": "是否启用此扩展的所有功能"
|
||||
},
|
||||
"apiKey": {
|
||||
"type": "text",
|
||||
"label": "API 密钥",
|
||||
"placeholder": "sk-...",
|
||||
"description": "请输入您的 API 密钥(用于访问外部服务)"
|
||||
},
|
||||
"apiEndpoint": {
|
||||
"type": "text",
|
||||
"label": "API 端点",
|
||||
"placeholder": "https://api.example.com",
|
||||
"description": "API 服务器地址"
|
||||
},
|
||||
"maxRetries": {
|
||||
"type": "number",
|
||||
"label": "最大重试次数",
|
||||
"min": 1,
|
||||
"max": 10,
|
||||
"step": 1,
|
||||
"description": "请求失败时的最大重试次数"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"label": "超时时间(秒)",
|
||||
"min": 5,
|
||||
"max": 300,
|
||||
"step": 5,
|
||||
"description": "API 请求的超时时间"
|
||||
},
|
||||
"temperature": {
|
||||
"type": "slider",
|
||||
"label": "温度",
|
||||
"min": 0,
|
||||
"max": 2,
|
||||
"step": 0.1,
|
||||
"description": "控制生成内容的随机性(0=确定性,2=高随机性)"
|
||||
},
|
||||
"model": {
|
||||
"type": "select",
|
||||
"label": "选择模型",
|
||||
"options": [
|
||||
{ "label": "GPT-4", "value": "gpt-4" },
|
||||
{ "label": "GPT-3.5 Turbo", "value": "gpt-3.5-turbo" },
|
||||
{ "label": "Claude 3", "value": "claude-3" }
|
||||
],
|
||||
"description": "选择要使用的 AI 模型"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "textarea",
|
||||
"label": "系统提示词",
|
||||
"rows": 4,
|
||||
"placeholder": "输入系统提示词...",
|
||||
"description": "用于指导 AI 行为的系统提示词"
|
||||
},
|
||||
"logLevel": {
|
||||
"type": "select",
|
||||
"label": "日志级别",
|
||||
"options": [
|
||||
{ "label": "调试", "value": "debug" },
|
||||
{ "label": "信息", "value": "info" },
|
||||
{ "label": "警告", "value": "warn" },
|
||||
{ "label": "错误", "value": "error" }
|
||||
],
|
||||
"description": "控制台日志的详细程度"
|
||||
},
|
||||
"autoSave": {
|
||||
"type": "boolean",
|
||||
"label": "自动保存",
|
||||
"description": "自动保存对话历史"
|
||||
},
|
||||
"saveInterval": {
|
||||
"type": "number",
|
||||
"label": "保存间隔(秒)",
|
||||
"min": 10,
|
||||
"max": 600,
|
||||
"step": 10,
|
||||
"description": "自动保存的时间间隔"
|
||||
}
|
||||
},
|
||||
"dependencies": {},
|
||||
"conflicts": []
|
||||
}
|
||||
2684
docs/重构进度管理.md
2684
docs/重构进度管理.md
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user