From fd660c8804ed4c93a9c13fa6122b139944e926be Mon Sep 17 00:00:00 2001 From: Echo <1711788888@qq.com> Date: Mon, 2 Mar 2026 00:50:56 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20=E4=BC=98=E5=8C=96=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E5=8D=A1=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=AD=A3=E5=88=99=E7=BC=96=E8=BE=91=E5=92=8C=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Echo <1711788888@qq.com> --- server/service/app/character.go | 15 +- web-app/src/components/ChatArea.tsx | 4 +- web-app/src/pages/CharacterManagePage.tsx | 261 ++++++++++++++++++++-- 3 files changed, 256 insertions(+), 24 deletions(-) diff --git a/server/service/app/character.go b/server/service/app/character.go index ed33f9b..a6f2ba8 100644 --- a/server/service/app/character.go +++ b/server/service/app/character.go @@ -427,14 +427,23 @@ func (s *CharacterService) processRegexScriptsFromExtensions(userID, characterID OwnerCharID: &characterID, } - // 提取字段 - if name, ok := scriptData["name"].(string); ok { + // 提取字段 - 兼容 SillyTavern 的字段名 + // 脚本名称:优先使用 scriptName,其次 name + if scriptName, ok := scriptData["scriptName"].(string); ok { + script.Name = scriptName + } else if name, ok := scriptData["name"].(string); ok { script.Name = name } + + // 查找正则表达式 if findRegex, ok := scriptData["findRegex"].(string); ok { script.FindRegex = findRegex } - if replaceWith, ok := scriptData["replaceWith"].(string); ok { + + // 替换字符串:优先使用 replaceString,其次 replaceWith + if replaceString, ok := scriptData["replaceString"].(string); ok { + script.ReplaceWith = replaceString + } else if replaceWith, ok := scriptData["replaceWith"].(string); ok { script.ReplaceWith = replaceWith } if placement, ok := scriptData["placement"].(float64); ok { diff --git a/web-app/src/components/ChatArea.tsx b/web-app/src/components/ChatArea.tsx index 6b43c40..45f0236 100644 --- a/web-app/src/components/ChatArea.tsx +++ b/web-app/src/components/ChatArea.tsx @@ -627,7 +627,7 @@ export default function ChatArea({ conversation, character, onConversationUpdate )} -
+
{/* 助手名称 */} {msg.role === 'assistant' && ( {character.name} @@ -638,7 +638,7 @@ export default function ChatArea({ conversation, character, onConversationUpdate className={`relative px-4 py-3 rounded-2xl ${ msg.role === 'user' ? 'bg-primary/25 border border-primary/20 rounded-br-md' - : 'glass rounded-bl-md' + : 'glass rounded-bl-md w-full' }`} style={{ wordBreak: 'break-word', overflowWrap: 'anywhere' }} > diff --git a/web-app/src/pages/CharacterManagePage.tsx b/web-app/src/pages/CharacterManagePage.tsx index e6cfe0b..9fe8c3d 100644 --- a/web-app/src/pages/CharacterManagePage.tsx +++ b/web-app/src/pages/CharacterManagePage.tsx @@ -1,7 +1,7 @@ import {useEffect, useState} from 'react' import {useNavigate, useSearchParams} from 'react-router-dom' import Navbar from '../components/Navbar' -import {Book, Code2, Download, Edit, FileJson, Image as ImageIcon, Plus, Search, Trash2, X} from 'lucide-react' +import {Book, Code2, Download, Edit, FileJson, FileUp, Image as ImageIcon, Plus, Search, Trash2, X} from 'lucide-react' import {type Character, characterApi} from '../api/character' import {type RegexScript, regexScriptApi} from '../api/regex' @@ -36,6 +36,14 @@ export default function CharacterManagePage() { const [showRegexScriptEditor, setShowRegexScriptEditor] = useState(false) const [regexScripts, setRegexScripts] = useState([]) const [editingTab, setEditingTab] = useState<'basic' | 'worldbook' | 'regex'>('basic') + const [showAddRegexModal, setShowAddRegexModal] = useState(false) + const [newRegexForm, setNewRegexForm] = useState({ + name: '', + findRegex: '', + replaceWith: '', + placement: 1, + order: 100, + }) const navigate = useNavigate() const [searchParams] = useSearchParams() @@ -759,13 +767,18 @@ export default function CharacterManagePage() { try { await regexScriptApi.createRegexScript({ name: '新脚本', - findRegex: '', + findRegex: '.*', // 默认匹配所有内容 + replaceWith: '', scope: 1, ownerCharId: selectedCharacter.id, + disabled: false, + placement: 1, // 默认输出阶段 + order: 100, }) loadRegexScripts(selectedCharacter.id) } catch (err: any) { - alert(err.response?.data?.msg || '创建失败') + console.error('创建脚本失败:', err) + alert(err.response?.data?.msg || err.message || '创建失败') } }} className="px-4 py-2 bg-gradient-to-r from-primary to-secondary rounded-lg text-sm cursor-pointer" @@ -913,10 +926,19 @@ export default function CharacterManagePage() { if (!testInput) return try { - const result = await regexScriptApi.testRegexScript(script.id, testInput) - alert(`原文:${result.data.original}\n\n结果:${result.data.result}`) + const response = await regexScriptApi.testRegexScript(script.id, testInput) + console.log('测试响应:', response) + + // 处理响应数据结构 + const testResult = response.data || response + if (testResult.success === false && testResult.error) { + alert(`测试失败:${testResult.error}`) + } else { + alert(`原文:${testResult.original}\n\n结果:${testResult.result}`) + } } catch (err: any) { - alert(err.response?.data?.msg || '测试失败') + console.error('测试失败:', err) + alert(err.response?.data?.msg || err.message || '测试失败') } }} className="px-3 py-1.5 glass-hover rounded-lg text-xs cursor-pointer" @@ -953,19 +975,7 @@ export default function CharacterManagePage() {
)} + + {/* 添加正则脚本弹窗 */} + {showAddRegexModal && selectedCharacter && ( +
+
+
+

添加正则脚本

+ +
+ +
+ {/* 导入 JSON 文件 */} +
+ + +

+ 支持 SillyTavern 格式的正则脚本 JSON 文件 +

+
+ +
+
+
+
+
+ 或手动创建 +
+
+ + {/* 手动输入表单 */} +
+
+ + setNewRegexForm({ ...newRegexForm, name: e.target.value })} + placeholder="例如: 移除 Markdown 加粗" + className="w-full px-4 py-3 glass rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" + /> +
+ +
+ + setNewRegexForm({ ...newRegexForm, findRegex: e.target.value })} + placeholder="例如: \*\*(.+?)\*\*" + className="w-full px-4 py-3 glass rounded-xl text-sm font-mono focus:outline-none focus:ring-2 focus:ring-primary/50" + /> +
+ +
+ + setNewRegexForm({ ...newRegexForm, replaceWith: e.target.value })} + placeholder="例如: $1" + className="w-full px-4 py-3 glass rounded-xl text-sm font-mono focus:outline-none focus:ring-2 focus:ring-primary/50" + /> +
+ +
+
+ + +
+ +
+ + setNewRegexForm({ ...newRegexForm, order: parseInt(e.target.value) })} + className="w-full px-4 py-3 glass rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" + /> +
+
+
+ +
+ + +
+
+
+
+ )}
) }