diff --git a/backend/api/route.py b/backend/api/route.py
index d19ee1d..8d8a633 100644
--- a/backend/api/route.py
+++ b/backend/api/route.py
@@ -5,16 +5,64 @@ from ..core.models.chat_history import ChatHistory # 修改导入语句
router = APIRouter()
-# 1. 从本地读取所有的data内容
+# 从本地读取所有的data内容
@router.get("/tool_bar/get_all_role_and_chat")
def get_all_role_and_chat_endpoint():
# 正确调用函数并返回结果
return get_all_role_and_chat()
-# 2. 根据rolename和chatname读取特定聊天记录
+# 根据rolename和chatname读取特定聊天记录
@router.get("/chat_box/get_chat_history")
async def get_chat_history_endpoint(role_name: str, chat_name: str):
# 实例化工具类
reader = ChatHistory.load_from_file(role_name, chat_name)
return reader.to_chatbox_format()
+
+
+# 从本地读取所有的预设列表内容
+@router.get("/presets/list")
+async def get_presets_list():
+ """
+ 获取所有可用的预设列表
+ 返回预设名称列表
+ """
+ import os
+ from pathlib import Path
+
+ # 预设文件存储目录
+ preset_dir = Path("data/preset")
+
+ # 确保目录存在
+ if not preset_dir.exists():
+ return {"presets": []}
+
+ # 获取所有.json文件
+ preset_files = list(preset_dir.glob("*.json"))
+
+ # 提取文件名(不带扩展名)作为预设名称
+ presets = [file.stem for file in preset_files]
+
+ return {"presets": presets}
+
+
+# 从本地读取特定预设
+@router.get("/presets/{preset_name}")
+async def get_preset_content(preset_name: str):
+ """
+ 获取特定预设的完整内容
+ """
+ import os
+ from pathlib import Path
+ import json
+
+ preset_path = Path("data/preset") / f"{preset_name}.json"
+
+ if not preset_path.exists():
+ raise HTTPException(status_code=404, detail="Preset not found")
+
+ with open(preset_path, 'r', encoding='utf-8') as f:
+ preset_data = json.load(f)
+
+ return preset_data
+
diff --git a/backend/core/models/PromptList.py b/backend/core/models/PromptList.py
index 8080d64..4d4bd8f 100644
--- a/backend/core/models/PromptList.py
+++ b/backend/core/models/PromptList.py
@@ -15,8 +15,8 @@ class AIDesignSpec(BaseModel):
top_a: float = Field(0.0, description="基于平方概率分布的采样")
min_p: float = Field(0.0, description="最小概率阈值")
repetition_penalty: float = Field(1.0, description="重复惩罚系数(1.0-1.2)")
- openai_max_context: int = Field(2048, description="上下文窗口大小(Token上限)")
- openai_max_tokens: int = Field(250, description="单次回复的最大长度")
+ max_context: int = Field(2048, description="上下文窗口大小(Token上限)")
+ max_tokens: int = Field(250, description="单次回复的最大长度")
max_context_unlocked: bool = Field(False, description="是否允许超出限制的上下文")
names_behavior: int = Field(0, description="名字处理行为(0=默认,1=始终包含,2=仅角色)")
send_if_empty: str = Field("", description="用户发送空消息时自动填充的内容")
@@ -30,7 +30,7 @@ class AIDesignSpec(BaseModel):
scenario_format: str = Field("{{scenario}}", description="场景描述的格式化字符串")
personality_format: str = Field("", description="角色性格的格式化字符串")
group_nudge_prompt: str = Field("", description="群组聊天中提示AI仅以特定角色回复的提示词")
- stream_openai: bool = Field(True, description="是否使用流式输出")
+ stream: bool = Field(True, description="是否使用流式输出")
assistant_prefill: str = Field("", description="强制AI回复的开头内容")
assistant_impersonation: str = Field("", description="模仿模式下强制AI回复的开头内容")
use_sysprompt: bool = Field(True, description="是否强制将提示词注入系统层")
diff --git a/frontend-react/src/Store/Slices/LeftTabsSlice.jsx b/frontend-react/src/Store/Slices/LeftTabsSlice.jsx
deleted file mode 100644
index e69de29..0000000
diff --git a/frontend-react/src/Store/Slices/LeftTabsSlices/PresetSlice.jsx b/frontend-react/src/Store/Slices/LeftTabsSlices/PresetSlice.jsx
new file mode 100644
index 0000000..15ba181
--- /dev/null
+++ b/frontend-react/src/Store/Slices/LeftTabsSlices/PresetSlice.jsx
@@ -0,0 +1,119 @@
+import { create } from 'zustand';
+
+const usePresetStore = create((set, get) => ({
+ // 预设选择
+ selectedPreset: '',
+
+ // 核心参数
+ parameters: {
+ temperature: 1.0,
+ frequency_penalty: 0.0,
+ presence_penalty: 0.0,
+ top_p: 1.0,
+ top_k: 0,
+ max_context: 1000000,
+ max_tokens: 30000,
+ max_context_unlocked: false,
+ stream_openai: true,
+ seed: -1,
+ n: 1
+ },
+
+ // 可用的预设列表 - 初始为空,将从后端加载
+ presets: [],
+
+ // 是否正在加载预设列表
+ isLoadingPresets: false,
+
+ // 参数设置折叠状态
+ isParametersExpanded: true,
+
+ // 从后端加载预设列表
+ fetchPresets: async () => {
+ set({ isLoadingPresets: true });
+ try {
+ const response = await fetch('/api/presets/list');
+ const data = await response.json();
+
+ // 转换为预设对象数组
+ const presetList = data.presets.map(name => ({
+ id: name,
+ name,
+ parameters: {} // 参数将在选择预设时加载
+ }));
+
+ set({ presets: presetList, isLoadingPresets: false });
+ } catch (error) {
+ console.error('Failed to fetch presets:', error);
+ set({ isLoadingPresets: false });
+ }
+ },
+
+ // 设置选中的预设
+ setSelectedPreset: async (presetId) => {
+ try {
+ // 从后端获取预设的完整内容
+ const response = await fetch(`/api/presets/${presetId}`);
+ const presetData = await response.json();
+
+ // 提取参数并更新状态
+ const parameters = {
+ temperature: presetData.temperature || 1.0,
+ frequency_penalty: presetData.frequency_penalty || 0.0,
+ presence_penalty: presetData.presence_penalty || 0.0,
+ top_p: presetData.top_p || 1.0,
+ top_k: presetData.top_k || 0,
+ max_context: presetData.openai_max_context || 1000000,
+ max_tokens: presetData.openai_max_tokens || 30000,
+ max_context_unlocked: presetData.max_context_unlocked || false,
+ stream_openai: presetData.stream_openai || true,
+ seed: presetData.seed || -1,
+ n: presetData.n || 1
+ };
+
+ set({
+ selectedPreset: presetId,
+ parameters
+ });
+ } catch (error) {
+ console.error('Failed to load preset:', error);
+ }
+ },
+
+ // 更新参数
+ updateParameter: ({ name, value }) => set((state) => ({
+ parameters: { ...state.parameters, [name]: value }
+ })),
+
+ // 添加预设
+ addPreset: (preset) => set((state) => ({
+ presets: [...state.presets, preset]
+ })),
+
+ // 保存当前设置为预设
+ saveCurrentAsPreset: ({ name }) => set((state) => {
+ const newPreset = {
+ id: `preset_${Date.now()}`,
+ name,
+ parameters: { ...state.parameters }
+ };
+ return {
+ presets: [...state.presets, newPreset],
+ selectedPreset: newPreset.id
+ };
+ }),
+
+ // 编辑预设名称
+ editPresetName: (presetId, newName) => set((state) => ({
+ presets: state.presets.map(preset =>
+ preset.id === presetId ? { ...preset, name: newName } : preset
+ )
+ })),
+
+ // 切换参数设置折叠状态
+ toggleParametersExpanded: () => set((state) => ({
+ isParametersExpanded: !state.isParametersExpanded
+ }))
+}));
+
+export default usePresetStore;
diff --git a/frontend-react/src/Store/Slices/SideBarLeftSlice.jsx b/frontend-react/src/Store/Slices/LeftTabsSlices/SideBarLeftSlice.jsx
similarity index 100%
rename from frontend-react/src/Store/Slices/SideBarLeftSlice.jsx
rename to frontend-react/src/Store/Slices/LeftTabsSlices/SideBarLeftSlice.jsx
diff --git a/frontend-react/src/Store/Slices/SideBarRightSlice.jsx b/frontend-react/src/Store/Slices/RightTabsSlices/SideBarRightSlice.jsx
similarity index 100%
rename from frontend-react/src/Store/Slices/SideBarRightSlice.jsx
rename to frontend-react/src/Store/Slices/RightTabsSlices/SideBarRightSlice.jsx
diff --git a/frontend-react/src/Store/indexStore.jsx b/frontend-react/src/Store/indexStore.jsx
index 08e14dd..29412f8 100644
--- a/frontend-react/src/Store/indexStore.jsx
+++ b/frontend-react/src/Store/indexStore.jsx
@@ -1,5 +1,5 @@
// frontend-react/src/store/index.js
export { default as useRoleSelectorStore } from './Slices/RoleSelectorSlice';
-export { default as useSideBarLeftStore } from './Slices/SideBarLeftSlice';
-export { default as useSideBarRightStore } from './Slices/SideBarRightSlice';
+export { default as useSideBarLeftStore } from './Slices/LeftTabsSlices/SideBarLeftSlice';
+export { default as useSideBarRightStore } from './Slices/RightTabsSlices/SideBarRightSlice';
export { default as useChatBoxStore } from './Slices/ChatBoxSlice';
\ No newline at end of file
diff --git a/frontend-react/src/components/DicePanel/DicePanel.jsx b/frontend-react/src/components/DicePanel/DicePanel.jsx
deleted file mode 100644
index c232e78..0000000
--- a/frontend-react/src/components/DicePanel/DicePanel.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const DicePanel = () => {
- return (
-
- );
-};
-
-export default DicePanel;
diff --git a/frontend-react/src/components/ImageDisplay/ImageDisplay.jsx b/frontend-react/src/components/ImageDisplay/ImageDisplay.jsx
deleted file mode 100644
index ab88d3c..0000000
--- a/frontend-react/src/components/ImageDisplay/ImageDisplay.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-const ImageDisplay = () => {
- return (
-
- );
-};
-
-export default ImageDisplay;
diff --git a/frontend-react/src/components/PresetPanel/PresetPanel.css b/frontend-react/src/components/PresetPanel/PresetPanel.css
deleted file mode 100644
index 58f50e5..0000000
--- a/frontend-react/src/components/PresetPanel/PresetPanel.css
+++ /dev/null
@@ -1,57 +0,0 @@
-/* ==================== 顶部工具栏区域 ==================== */
-
-.toolbar {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 50px;
- background-color: #fff;
- border-bottom: 1px solid #ddd;
- padding: 0;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- z-index: 100;
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
- transition: height 0.3s ease;
-}
-
-.toolbar.expanded {
- height: auto;
- max-height: 300px;
- overflow-y: auto;
- padding-bottom: 10px;
-}
-
-.toolbar-content {
- display: none;
- width: 100%;
- padding: 10px 20px;
-}
-
-.toolbar.expanded .toolbar-content {
- display: block;
-}
-
-.toolbar-toggle-btn {
- position: absolute;
- right: 0;
- top: 0;
- height: 50px;
- width: 50px;
- background: none;
- border: none;
- border-left: 1px solid #ddd;
- cursor: pointer;
- font-size: 0.8rem;
- color: #666;
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 101;
-}
-
-.toolbar-toggle-btn:hover {
- background-color: #f5f5f5;
-}
diff --git a/frontend-react/src/components/PresetPanel/PresetPanel.jsx b/frontend-react/src/components/PresetPanel/PresetPanel.jsx
deleted file mode 100644
index 10c068b..0000000
--- a/frontend-react/src/components/PresetPanel/PresetPanel.jsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import './PresetPanel.css';
-
-const PresetPanel = () => {
- return (
-
- {/* 顶部:预设选择与输入 */}
-
- {/* 下拉框 */}
-
- {/* 输入框组(待定) */}
-
- {/* inputs here */}
-
-
-
- {/* 下方:大槽位区域 */}
-
- {/* 槽位列表 */}
-
槽位 1
-
槽位 2
- {/* ... */}
-
-
- );
-};
-
-export default PresetPanel;
diff --git a/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx b/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx
index f2cfaa2..f950b4b 100644
--- a/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx
+++ b/frontend-react/src/components/SideBarLeft/SideBarLeft.jsx
@@ -2,7 +2,7 @@
import React from 'react';
import './SideBarLeft.css';
import { useSideBarLeftStore } from '../../Store/indexStore';
-import useSideBarRightStore from '../../Store/Slices/SideBarLeftSlice';
+import useSideBarRightStore from '../../Store/Slices/LeftTabsSlices/SideBarLeftSlice';
import Gallery from './tab/Gallery';
import ApiConfig from './tab/ApiConfig';
import Presets from './tab/Presets';
diff --git a/frontend-react/src/components/SideBarLeft/tab/Presets.jsx b/frontend-react/src/components/SideBarLeft/tab/Presets.jsx
index 5b033fb..d44b5b4 100644
--- a/frontend-react/src/components/SideBarLeft/tab/Presets.jsx
+++ b/frontend-react/src/components/SideBarLeft/tab/Presets.jsx
@@ -1,13 +1,523 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
+import usePresetStore from '../../../Store/Slices/LeftTabsSlices/PresetSlice';
import '../tabcss/Presets.css';
-const Presets = () => {
+const PresetPanel = () => {
+ const {
+ selectedPreset,
+ parameters,
+ presets,
+ isLoadingPresets,
+ setSelectedPreset,
+ updateParameter,
+ saveCurrentAsPreset,
+ editPresetName: updatePresetName,
+ isParametersExpanded,
+ toggleParametersExpanded,
+ fetchPresets
+ } = usePresetStore();
+ const [showSaveDialog, setShowSaveDialog] = useState(false);
+ const [showEditDialog, setShowEditDialog] = useState(false);
+ const [showImportDialog, setShowImportDialog] = useState(false);
+ const [newPresetName, setNewPresetName] = useState('');
+ const [editPresetId, setEditPresetId] = useState('');
+ const [editPresetName, setEditPresetName] = useState('');
+ const [importPresetData, setImportPresetData] = useState('');
+ const [tooltip, setTooltip] = useState({ visible: false, content: '', x: 0, y: 0 });
+
+ // 参数描述映射
+ const parameterDescriptions = {
+ temperature: "生成温度,控制随机性(0-2)",
+ frequency_penalty: "频率惩罚,降低重复token概率",
+ presence_penalty: "存在惩罚,鼓励谈论新话题",
+ top_p: "核采样,控制词汇选择范围",
+ top_k: "随机采样范围,从概率最高的K个词中选择",
+ max_context: "上下文窗口大小(Token上限)",
+ max_tokens: "单次回复的最大长度",
+ max_context_unlocked: "是否允许超出限制的上下文",
+ stream_openai: "是否使用流式输出",
+ seed: "随机种子(-1为随机)",
+ n: "生成回复的数量"
+ };
+
+ // 显示工具提示
+ const showTooltip = (event, content) => {
+ setTooltip({
+ visible: true,
+ content,
+ x: event.clientX,
+ y: event.clientY
+ });
+ };
+
+ // 隐藏工具提示
+ const hideTooltip = () => {
+ setTooltip({ ...tooltip, visible: false });
+ };
+
+ // 处理参数更新
+ const handleParameterChange = (name, value) => {
+ // 根据参数类型转换值
+ let convertedValue = value;
+ if (name === 'temperature' || name === 'frequency_penalty' || name === 'presence_penalty' || name === 'top_p') {
+ convertedValue = parseFloat(value);
+ } else if (name === 'top_k' || name === 'max_context' || name === 'max_tokens' || name === 'seed' || name === 'n') {
+ convertedValue = parseInt(value, 10);
+ } else if (name === 'max_context_unlocked' || name === 'stream_openai') {
+ convertedValue = value;
+ }
+
+ updateParameter({ name, value: convertedValue });
+ };
+
+ useEffect(() => {
+ fetchPresets();
+ }, [fetchPresets]);
+
+ // 保存当前设置为预设
+ const handleSavePreset = () => {
+ if (newPresetName.trim()) {
+ saveCurrentAsPreset({ name: newPresetName });
+ setNewPresetName('');
+ setShowSaveDialog(false);
+ }
+ };
+
+ // 编辑预设名称
+ const handleEditPreset = () => {
+ if (editPresetId && editPresetName.trim()) {
+ updatePresetName(editPresetId, editPresetName);
+ setEditPresetId('');
+ setEditPresetName('');
+ setShowEditDialog(false);
+ }
+ };
+
+ // 导入预设
+ const handleImportPreset = () => {
+ try {
+ const importedPreset = JSON.parse(importPresetData);
+ if (importedPreset.name && importedPreset.parameters) {
+ saveCurrentAsPreset({ name: importedPreset.name });
+ // 这里需要更新参数
+ Object.keys(importedPreset.parameters).forEach(key => {
+ updateParameter({ name: key, value: importedPreset.parameters[key] });
+ });
+ setImportPresetData('');
+ setShowImportDialog(false);
+ }
+ } catch (error) {
+ console.error('导入预设失败:', error);
+ }
+ };
+
+ // 导出预设
+ const handleExportPreset = () => {
+ if (selectedPreset) {
+ const preset = presets.find(p => p.id === selectedPreset);
+ if (preset) {
+ const dataStr = JSON.stringify(preset, null, 2);
+ const dataBlob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(dataBlob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `${preset.name}.json`;
+ link.click();
+ URL.revokeObjectURL(url);
+ }
+ }
+ };
+
return (
-
-
预设配置
- {/* 在这里实现预设配置的具体内容 */}
+
+ {/* 工具提示 */}
+ {tooltip.visible && (
+
+ {tooltip.content}
+
+ )}
+
+ {/* 顶部:预设选择与操作 */}
+
+ {/* 预设选择下拉框 */}
+
+
+
+
+
+ {/* 操作按钮 */}
+
+
+
+
+
+
+
+
+ {/* 保存预设对话框 */}
+ {showSaveDialog && (
+
+
setNewPresetName(e.target.value)}
+ placeholder="预设名称"
+ />
+
+
+
+
+
+ )}
+
+ {/* 编辑预设对话框 */}
+ {showEditDialog && (
+
+
setEditPresetName(e.target.value)}
+ placeholder="预设名称"
+ />
+
+
+
+
+
+ )}
+
+ {/* 导入预设对话框 */}
+ {showImportDialog && (
+
+ )}
+
+ {/* 参数设置区域 */}
+
+
+ 参数设置
+ ▼
+
+
+ {isParametersExpanded && (
+
+ {/* 温度滑块 */}
+
+
+ handleParameterChange('temperature', e.target.value)}
+ className="parameter-slider"
+ />
+ handleParameterChange('temperature', e.target.value)}
+ className="parameter-number"
+ />
+
+
+ {/* 频率惩罚滑块 */}
+
+
+ handleParameterChange('frequency_penalty', e.target.value)}
+ className="parameter-slider"
+ />
+ handleParameterChange('frequency_penalty', e.target.value)}
+ className="parameter-number"
+ />
+
+
+ {/* 存在惩罚滑块 */}
+
+
+ handleParameterChange('presence_penalty', e.target.value)}
+ className="parameter-slider"
+ />
+ handleParameterChange('presence_penalty', e.target.value)}
+ className="parameter-number"
+ />
+
+
+ {/* Top P 滑块 */}
+
+
+ handleParameterChange('top_p', e.target.value)}
+ className="parameter-slider"
+ />
+ handleParameterChange('top_p', e.target.value)}
+ className="parameter-number"
+ />
+
+
+ {/* Top K 输入框 */}
+
+
+ handleParameterChange('top_k', e.target.value)}
+ className="parameter-input"
+ />
+
+
+ {/* 最大上下文输入框 */}
+
+
+ handleParameterChange('max_context', e.target.value)}
+ className="parameter-input"
+ />
+
+
+ {/* 最大Token输入框 */}
+
+
+ handleParameterChange('max_tokens', e.target.value)}
+ className="parameter-input"
+ />
+
+
+ {/* 随机种子输入框 */}
+
+
+ handleParameterChange('seed', e.target.value)}
+ className="parameter-input"
+ />
+
+
+ {/* 生成数量输入框 */}
+
+
+ handleParameterChange('n', e.target.value)}
+ className="parameter-input"
+ />
+
+
+ {/* 开关选项 */}
+
+
+ )}
+
);
};
-export default Presets;
+export default PresetPanel;
diff --git a/frontend-react/src/components/SideBarLeft/tabcss/Presets.css b/frontend-react/src/components/SideBarLeft/tabcss/Presets.css
index e69de29..7b99c48 100644
--- a/frontend-react/src/components/SideBarLeft/tabcss/Presets.css
+++ b/frontend-react/src/components/SideBarLeft/tabcss/Presets.css
@@ -0,0 +1,302 @@
+/* ==================== 预设页区域 ==================== */
+
+.preset-panel {
+ display: flex;
+ flex-direction: column;
+ padding: 12px;
+ background-color: #f9f9f9;
+ border-radius: 6px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
+ height: 100%;
+ overflow-y: auto;
+}
+
+.preset-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #eaeaea;
+}
+
+.preset-select-container {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ margin-right: 8px;
+}
+
+.preset-label {
+ margin-right: 8px;
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ cursor: help;
+}
+
+.preset-select {
+ flex: 1;
+ padding: 6px 10px;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ background-color: white;
+ font-size: 14px;
+ color: #333;
+ transition: border-color 0.2s;
+}
+
+.preset-select:focus {
+ outline: none;
+ border-color: #4a6cf7;
+}
+
+.preset-actions {
+ display: flex;
+ gap: 4px;
+}
+
+.preset-action-btn {
+ background: none;
+ border: none;
+ font-size: 16px;
+ cursor: pointer;
+ padding: 6px;
+ border-radius: 4px;
+ transition: background-color 0.2s;
+ color: #555;
+}
+
+.preset-action-btn:hover {
+ background-color: #e8e8e8;
+ color: #333;
+}
+
+.preset-save-dialog,
+.preset-edit-dialog,
+.preset-import-dialog {
+ display: flex;
+ flex-direction: column;
+ padding: 12px;
+ background-color: white;
+ border-radius: 6px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ margin-bottom: 16px;
+}
+
+.preset-save-dialog input,
+.preset-edit-dialog input {
+ padding: 8px;
+ margin-bottom: 10px;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ font-size: 14px;
+ transition: border-color 0.2s;
+}
+
+.preset-save-dialog input:focus,
+.preset-edit-dialog input:focus {
+ outline: none;
+ border-color: #4a6cf7;
+}
+
+.preset-import-dialog textarea {
+ padding: 8px;
+ margin-bottom: 10px;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ resize: vertical;
+ font-family: monospace;
+ font-size: 13px;
+ min-height: 100px;
+ transition: border-color 0.2s;
+}
+
+.preset-import-dialog textarea:focus {
+ outline: none;
+ border-color: #4a6cf7;
+}
+
+.dialog-buttons {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.dialog-buttons button {
+ margin-left: 8px;
+ padding: 6px 12px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: background-color 0.2s;
+}
+
+.dialog-buttons button:first-child {
+ background-color: #4a6cf7;
+ color: white;
+}
+
+.dialog-buttons button:first-child:hover {
+ background-color: #3a5ce5;
+}
+
+.dialog-buttons button:last-child {
+ background-color: #f0f0f0;
+ color: #333;
+}
+
+.dialog-buttons button:last-child:hover {
+ background-color: #e0e0e0;
+}
+
+.preset-parameters-container {
+ border: 1px solid #eaeaea;
+ border-radius: 6px;
+ overflow: hidden;
+}
+
+.parameters-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 12px;
+ background-color: #f5f5f5;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ transition: background-color 0.2s;
+}
+
+.parameters-header:hover {
+ background-color: #eaeaea;
+}
+
+.expand-icon {
+ transition: transform 0.3s ease;
+ font-size: 12px;
+}
+
+.expand-icon.expanded {
+ transform: rotate(180deg);
+}
+
+.preset-parameters {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 12px;
+}
+
+.parameter-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.parameter-label {
+ width: 140px;
+ font-size: 13px;
+ font-weight: 500;
+ color: #555;
+ cursor: help;
+}
+
+.parameter-slider {
+ flex: 1;
+ height: 4px;
+ border-radius: 2px;
+ background: #e0e0e0;
+ outline: none;
+ -webkit-appearance: none;
+}
+
+.parameter-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ background: #4a6cf7;
+ cursor: pointer;
+}
+
+.parameter-slider::-moz-range-thumb {
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ background: #4a6cf7;
+ cursor: pointer;
+ border: none;
+}
+
+.parameter-number {
+ width: 60px;
+ padding: 4px 6px;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ text-align: center;
+ font-size: 13px;
+ transition: border-color 0.2s;
+}
+
+.parameter-number:focus {
+ outline: none;
+ border-color: #4a6cf7;
+}
+
+.parameter-input {
+ flex: 1;
+ padding: 6px 8px;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ font-size: 13px;
+ transition: border-color 0.2s;
+}
+
+.parameter-input:focus {
+ outline: none;
+ border-color: #4a6cf7;
+}
+
+.parameter-toggles {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-top: 8px;
+}
+
+.toggle-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.toggle-label {
+ font-size: 13px;
+ font-weight: 500;
+ color: #555;
+ cursor: help;
+}
+
+.toggle-checkbox {
+ width: 16px;
+ height: 16px;
+ cursor: pointer;
+ accent-color: #4a6cf7;
+}
+
+.tooltip {
+ position: fixed;
+ padding: 6px 10px;
+ background-color: #333;
+ color: white;
+ border-radius: 4px;
+ font-size: 12px;
+ max-width: 250px;
+ z-index: 1000;
+ pointer-events: none;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ line-height: 1.4;
+}
diff --git a/frontend-react/src/components/SideBarRight/SideBarRight.css b/frontend-react/src/components/SideBarRight/SideBarRight.css
index 9cfbcfa..c4965d4 100644
--- a/frontend-react/src/components/SideBarRight/SideBarRight.css
+++ b/frontend-react/src/components/SideBarRight/SideBarRight.css
@@ -69,7 +69,6 @@
border-bottom: 1px solid #f0f0f0;
}
-
.tab-content:last-child {
border-bottom: none;
}
@@ -99,4 +98,3 @@
.tab-content::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
-
diff --git a/frontend-react/src/components/SideBarRight/SideBarRight.jsx b/frontend-react/src/components/SideBarRight/SideBarRight.jsx
index ff480ba..d690483 100644
--- a/frontend-react/src/components/SideBarRight/SideBarRight.jsx
+++ b/frontend-react/src/components/SideBarRight/SideBarRight.jsx
@@ -1,16 +1,20 @@
import React, { useEffect } from 'react';
import './SideBarRight.css';
-import DicePanel from '../DicePanel/DicePanel';
-import ImageDisplay from '../ImageDisplay/ImageDisplay';
-import useSideBarRightStore from '../../Store/Slices/SideBarRightSlice';
+import Dice from './tab/Dice';
+import Debug from './tab/Debug';
+import Macros from './tab/Macros';
+import Table from './tab/Table';
+import useSideBarRightStore from '../../Store/Slices/RightTabsSlices/SideBarRightSlice';
const SideBarRight = () => {
const { selectedTabs, allTabs, handleTabClick, setTabComponent } = useSideBarRightStore();
// 设置标签组件
useEffect(() => {
- setTabComponent('dice', DicePanel);
- // 可以在这里设置其他标签的组件
+ setTabComponent('dice', Dice);
+ setTabComponent('debug', Debug);
+ setTabComponent('macros', Macros);
+ setTabComponent('table', Table);
}, [setTabComponent]);
return (
diff --git a/frontend-react/src/components/SideBarRight/tab/Debug.jsx b/frontend-react/src/components/SideBarRight/tab/Debug.jsx
new file mode 100644
index 0000000..e4639d8
--- /dev/null
+++ b/frontend-react/src/components/SideBarRight/tab/Debug.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+const Debug = () => {
+ return (
+
+
上下文调试
+
这是上下文调试面板的占位页面
+
+ );
+};
+
+export default Debug;
diff --git a/frontend-react/src/components/SideBarRight/tab/Dice.jsx b/frontend-react/src/components/SideBarRight/tab/Dice.jsx
new file mode 100644
index 0000000..6641bb1
--- /dev/null
+++ b/frontend-react/src/components/SideBarRight/tab/Dice.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+const Dice = () => {
+ return (
+
+ );
+};
+
+export default Dice;
diff --git a/frontend-react/src/components/SideBarRight/tab/Macros.jsx b/frontend-react/src/components/SideBarRight/tab/Macros.jsx
new file mode 100644
index 0000000..3cb5b87
--- /dev/null
+++ b/frontend-react/src/components/SideBarRight/tab/Macros.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+const Macros = () => {
+ return (
+
+ );
+};
+
+export default Macros;
diff --git a/frontend-react/src/components/SideBarRight/tab/Table.jsx b/frontend-react/src/components/SideBarRight/tab/Table.jsx
new file mode 100644
index 0000000..4064580
--- /dev/null
+++ b/frontend-react/src/components/SideBarRight/tab/Table.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+const Table = () => {
+ return (
+
+ );
+};
+
+export default Table;