feat: Add custom request body editor with persistent message storage

- Add CustomRequestEditor component with JSON validation and real-time formatting
- Implement bidirectional sync between chat messages and custom request body
- Add persistent local storage for chat messages (separate from config)
- Remove redundant System Prompt field in custom mode
- Refactor configuration storage to separate messages and settings

New Features:
• Custom request body mode with JSON editor and syntax highlighting
• Real-time bidirectional synchronization between chat UI and custom request body
• Persistent message storage that survives page refresh
• Enhanced configuration export/import including message data
• Improved parameter organization with collapsible sections

Technical Changes:
• Add loadMessages/saveMessages functions in configStorage
• Update usePlaygroundState hook to handle message persistence
• Refactor SettingsPanel to remove System Prompt in custom mode
• Add STORAGE_KEYS constants for better storage key management
• Implement debounced auto-save for both config and messages
• Add hash-based change detection to prevent unnecessary updates

UI/UX Improvements:
• Disabled state styling for parameters in custom mode
• Warning banners and visual feedback for mode switching
• Mobile-responsive design for custom request editor
• Consistent styling with existing design system
This commit is contained in:
Apple\Apple
2025-06-01 17:07:36 +08:00
parent ffdedde6ac
commit 5107f1b84a
17 changed files with 1199 additions and 380 deletions

View File

@@ -0,0 +1,70 @@
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { API, showError } from '../helpers/index.js';
import { API_ENDPOINTS } from '../utils/constants';
import { processModelsData, processGroupsData } from '../utils/apiUtils';
export const useDataLoader = (
userState,
inputs,
handleInputChange,
setModels,
setGroups
) => {
const { t } = useTranslation();
const loadModels = useCallback(async () => {
try {
const res = await API.get(API_ENDPOINTS.USER_MODELS);
const { success, message, data } = res.data;
if (success) {
const { modelOptions, selectedModel } = processModelsData(data, inputs.model);
setModels(modelOptions);
if (selectedModel !== inputs.model) {
handleInputChange('model', selectedModel);
}
} else {
showError(t(message));
}
} catch (error) {
showError(t('加载模型失败'));
}
}, [inputs.model, handleInputChange, setModels, t]);
const loadGroups = useCallback(async () => {
try {
const res = await API.get(API_ENDPOINTS.USER_GROUPS);
const { success, message, data } = res.data;
if (success) {
const userGroup = userState?.user?.group || JSON.parse(localStorage.getItem('user'))?.group;
const groupOptions = processGroupsData(data, userGroup);
setGroups(groupOptions);
const hasCurrentGroup = groupOptions.some(option => option.value === inputs.group);
if (!hasCurrentGroup) {
handleInputChange('group', groupOptions[0]?.value || '');
}
} else {
showError(t(message));
}
} catch (error) {
showError(t('加载分组失败'));
}
}, [userState, inputs.group, handleInputChange, setGroups, t]);
// 自动加载数据
useEffect(() => {
if (userState?.user) {
loadModels();
loadGroups();
}
}, [userState?.user, loadModels, loadGroups]);
return {
loadModels,
loadGroups
};
};