5 lines
18 KiB
JavaScript
5 lines
18 KiB
JavaScript
import{r as t,j as s}from"./router-zNjPR4CY.js";import{p as es}from"./parse-BBzU4wsN.js";import{U as ss,E as ts,V as rs,M as Ae,W as as,S as Pe,T as He,O as $e,P as ce,B as P,A as ns,m as os,n as is,o as cs,q as ls,r as us,s as ds,t as ms,v as ps,X as Le,Y as De,Z as Oe,_ as fs,g as gs,R as hs}from"./index-CuOHsLf7.js";import{C as xs}from"./CodeEditor-4JcGD1og.js";import{B as vs,A as bs,H as Ie,M as Cs,C as ys,a as ws,E as js,b as Ss,K as _s,R as ks,c as Ts,d as Ns,f as le,D as Rs}from"./BotInfoSection-D3nfOJyA.js";import"./textarea-8PIujbf-.js";import"./switch-Kc2EZ0Ga.js";import"./select-DGqIoF9r.js";import"./slider-mDhSC-zv.js";import{R as Es,u as Ms,a as As}from"./restart-overlay-B44c6hlE.js";import{af as Ps,ag as Ls,R as Ds,ah as Os,a6 as Is,ai as Ve,G as Vs,y as Bs}from"./icons-DTcdLw9j.js";import"./misc-BwRzHX8c.js";import"./radix-C-ZuImoP.js";import"./utils-DjBw3JGv.js";import"./badge-CDs67obV.js";import"./separator-B_DSOSdz.js";import"./key-value-editor-D1uUqXIn.js";import"./popover-DSuRLFDH.js";import"./progress-C6-hh8fF.js";import"./system-api-DeeJapvB.js";function Hs(h,x,d,l={}){const{debounceMs:m=2e3,onSaveSuccess:r,onSaveError:n}=l,a=t.useRef(null),v=t.useCallback(async(y,C)=>{try{x(!0);const w=await ss(y,C);if(!w.success)throw new Error(w.error);d(!1),r?.()}catch(w){console.error(`自动保存 ${y} 失败:`,w),d(!0),n?.(w instanceof Error?w:new Error(String(w)))}finally{x(!1)}},[x,d,r,n]),k=t.useCallback((y,C)=>{h||(d(!0),a.current&&clearTimeout(a.current),a.current=setTimeout(()=>{v(y,C)},m))},[h,d,v,m]),T=t.useCallback(async(y,C)=>{a.current&&(clearTimeout(a.current),a.current=null),await v(y,C)},[v]),E=t.useCallback(()=>{a.current&&(clearTimeout(a.current),a.current=null)},[]);return t.useEffect(()=>()=>{a.current&&clearTimeout(a.current)},[]),{triggerAutoSave:k,saveNow:T,cancelPendingAutoSave:E}}function g(h,x,d,l){const m=t.useRef(null);t.useEffect(()=>{if(!h)return;const r=JSON.stringify(h);if(d||m.current===null){m.current=r;return}r!==m.current&&(m.current=r,l(x,h))},[h,d])}const $s=500,Be=["bot","chat","expression","a_memorix","visual","message_receive","emoji","voice","response_post_process","webui","plugin_runtime","log"],A=new Set(["bot","chat","expression","a_memorix"]);function Fs(h){const x=h.nested||{},d=Object.entries(x),l=new Map,m=(r,n=new Set)=>{if(n.has(r))return null;const a=x[r];return a?a.uiParent?(n.add(r),m(a.uiParent,n)):a.uiLabel&&a.uiIcon?r:null:null};for(const[r,n]of d)n.uiLabel&&n.uiIcon&&!n.uiParent&&l.set(r,{id:r,label:n.uiLabel,icon:n.uiIcon||"",sections:[r]});for(const[r]of d){const n=m(r);if(!n||n===r)continue;const a=l.get(n);a&&!a.sections.includes(r)&&a.sections.push(r)}return Array.from(l.values()).sort((r,n)=>{const a=Be.indexOf(r.id),v=Be.indexOf(n.id);return(a===-1?1/0:a)-(v===-1?1/0:v)})}function dt(){return s.jsx(Es,{children:s.jsx(zs,{})})}function zs(){const[h,x]=t.useState(!0),[d,l]=t.useState(!1),[m,r]=t.useState(!1),[n,a]=t.useState(!1),[v,k]=t.useState("visual"),[T,E]=t.useState(""),[y,C]=t.useState(!1),[w,N]=t.useState(""),[ae,i]=t.useState(()=>localStorage.getItem("bot-config-restart-notice-dismissed")!=="true"),{toast:c}=ts(),{triggerRestart:L,isRestarting:M}=Ms(),[b,D]=t.useState(null),[j,O]=t.useState(null),[R,se]=t.useState(null),[I,ue]=t.useState(null),[V,de]=t.useState(null),[B,me]=t.useState(null),[H,pe]=t.useState(null),[$,fe]=t.useState(null),[F,ge]=t.useState(null),[z,he]=t.useState(null),[q,xe]=t.useState(null),[G,ve]=t.useState(null),[U,be]=t.useState(null),[W,Ce]=t.useState(null),[K,ye]=t.useState(null),[Y,we]=t.useState(null),[X,je]=t.useState(null),[J,Se]=t.useState(null),[Z,_e]=t.useState(null),[Q,ke]=t.useState(null),[ee,Te]=t.useState(null),[te,ze]=t.useState(null),u=t.useRef(!0),Ne=t.useRef({}),qe=e=>{const o=e.split(`
|
||
`);let f=o[0];f=f.replace(/^Error:\s*/,"");const S=[[/Invalid TOML document: unrecognized escape sequence/,"TOML 文档错误:无法识别的转义序列(提示:在双引号字符串中使用 \\\\ 转义反斜杠,或使用单引号字符串)"],[/Invalid TOML document: only letter, numbers, dashes and underscores are allowed in keys/,"TOML 文档错误:键名只能包含字母、数字、短横线和下划线"],[/Invalid TOML document: (.+)/,"TOML 文档错误:$1"],[/Unexpected character.*at line (\d+), column (\d+)/,"第 $1 行第 $2 列:意外的字符"],[/Expected.*at line (\d+), column (\d+)/,"第 $1 行第 $2 列:缺少必要的字符"],[/Invalid.*at line (\d+), column (\d+)/,"第 $1 行第 $2 列:无效的语法"],[/Unterminated string at line (\d+)/,"第 $1 行:字符串未正常结束(缺少引号)"],[/Duplicate key.*at line (\d+)/,"第 $1 行:重复的键名"],[/Invalid escape sequence at line (\d+)/,"第 $1 行:无效的转义序列(提示:在双引号字符串中使用 \\\\ 转义反斜杠)"],[/Expected.*but got.*at line (\d+)/,"第 $1 行:类型不匹配"],[/line (\d+), column (\d+)/,"第 $1 行第 $2 列"],[/Unexpected end of input/,"意外的文件结束(可能缺少闭合符号)"],[/Unexpected token/,"意外的标记"],[/Invalid number/,"无效的数字"],[/Invalid date/,"无效的日期格式"],[/Invalid boolean/,"无效的布尔值(应为 true 或 false)"],[/Unexpected character/,"意外的字符"],[/unrecognized escape sequence/,"无法识别的转义序列"]];for(const[_,Me]of S)if(_.test(f)){f=f.replace(_,Me);break}return o.length>1?(o[0]=f,o.join(`
|
||
`)):f},ne=t.useCallback(e=>{const{memory:o,...f}=e;Ne.current=f,D(e.bot??{}),O(e.personality??{}),se(e.chat??{}),ue(e.expression??{}),de(e.emoji??{}),me(e.visual??{}),pe(e.voice??{}),fe(e.message_receive??{}),ge(e.keyword_reaction??{}),he(e.response_post_process??{}),xe(e.chinese_typo??{}),ve(e.response_splitter??{}),be(e.log??{}),Ce(e.debug??{}),ye(e.maim_message??{}),we(e.telemetry??{}),je(e.webui??{}),Se(e.database??{}),_e(e.mcp??{}),ke(e.plugin_runtime??{}),Te(e.a_memorix??{})},[]),Re=t.useCallback(()=>({...Ne.current,bot:b,personality:j,chat:R,expression:I,emoji:V,visual:B,voice:H,message_receive:$,keyword_reaction:F,response_post_process:z,chinese_typo:q,response_splitter:G,log:U,debug:W,maim_message:K,telemetry:Y,webui:X,database:J,mcp:Z,plugin_runtime:Q,a_memorix:ee}),[b,j,R,I,V,B,H,$,F,z,q,G,U,W,K,Y,X,J,Z,Q,ee]),oe=t.useCallback(async()=>{try{const e=await rs();if(!e.success){c({variant:"destructive",title:"加载失败",description:e.error});return}const f=e.data.content.replace(/"([^"]*)"/g,(S,_)=>`"${_.replace(/\\n/g,`
|
||
`).replace(/\\t/g," ").replace(/\\r/g,"\r").replace(/\\"/g,'"').replace(/\\\\/g,"\\")}"`);E(f),C(!1)}catch(e){c({variant:"destructive",title:"加载失败",description:e instanceof Error?e.message:"加载源代码失败"})}},[c]),re=t.useCallback(async()=>{try{x(!0);const[e,o]=await Promise.all([Ae(),as()]);if(!e.success){c({title:"加载失败",description:e.error,variant:"destructive"}),x(!1);return}ne(e.data.config),o.success&&o.data&&ze(o.data.schema),a(!1),u.current=!1,await oe()}catch(e){console.error("加载配置失败:",e),c({title:"加载失败",description:"无法加载配置文件",variant:"destructive"})}finally{x(!1)}},[c,oe,ne]);t.useEffect(()=>{re()},[re]),t.useEffect(()=>{const e=[["bot.platform",vs,"replace"],["bot.alias_names",bs],["bot.qq_account",Ie,"hidden"],["bot.platforms",Ie,"hidden"],["personality.multiple_reply_style",Cs],["chat.chat_prompts",ys],["chat.talk_value_rules",ws],["expression.expression_groups",js],["expression.learning_list",Ss],["keyword_reaction.keyword_rules",_s],["keyword_reaction.regex_rules",ks],["mcp.client.roots.items",Ts],["mcp.servers",Ns]];for(const[o,f,S="replace"]of e)le.register(o,f,S);return()=>{for(const[o]of e)le.unregister(o)}},[]);const{triggerAutoSave:p,cancelPendingAutoSave:ie}=Hs(u.current,r,a);g(b,"bot",u.current,p),g(j,"personality",u.current,p),g(R,"chat",u.current,p),g(I,"expression",u.current,p),g(V,"emoji",u.current,p),g(B,"visual",u.current,p),g(H,"voice",u.current,p),g($,"message_receive",u.current,p),g(F,"keyword_reaction",u.current,p),g(z,"response_post_process",u.current,p),g(q,"chinese_typo",u.current,p),g(G,"response_splitter",u.current,p),g(U,"log",u.current,p),g(W,"debug",u.current,p),g(K,"maim_message",u.current,p),g(Y,"telemetry",u.current,p),g(X,"webui",u.current,p),g(J,"database",u.current,p),g(Z,"mcp",u.current,p),g(Q,"plugin_runtime",u.current,p),g(ee,"a_memorix",u.current,p);const Ge=async()=>{try{l(!0);const e=T.replace(/"([^"]*)"/g,(f,S)=>`"${S.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\r/g,"\\r")}"`);try{es(e)}catch(f){const S=f instanceof Error?f.message:"TOML 格式错误",_=qe(S);C(!0),N(_),c({variant:"destructive",title:"TOML 格式错误",description:_}),l(!1);return}const o=await fs(e);if(!o.success){C(!0);const f=o.error;N(f),c({variant:"destructive",title:"保存失败",description:f});return}a(!1),C(!1),N(""),c({title:"保存成功",description:"配置已保存"}),await re()}catch(e){C(!0);const o=e instanceof Error?e.message:"保存配置失败";N(o),c({variant:"destructive",title:"保存失败",description:o})}finally{l(!1)}},Ue=async e=>{if(n){c({variant:"destructive",title:"切换失败",description:"请先保存当前更改"});return}if(k(e),e==="source")await oe();else try{const o=await Ae();if(!o.success){c({title:"加载失败",description:o.error,variant:"destructive"});return}ne(o.data.config),a(!1)}catch(o){console.error("加载配置失败:",o),c({title:"加载失败",description:"无法加载配置文件",variant:"destructive"})}},We=async()=>{try{l(!0),ie();const e=await Oe(Re());if(!e.success){c({title:"保存失败",description:e.error,variant:"destructive"}),l(!1);return}a(!1),c({title:"保存成功",description:"麦麦设置已保存"})}catch(e){console.error("保存配置失败:",e),c({title:"保存失败",description:e.message,variant:"destructive"})}finally{l(!1)}},Ee=async()=>{await L()},Ke=()=>{localStorage.setItem("bot-config-restart-notice-dismissed","true"),i(!1)},Ye=async()=>{ie(),await re(),a(!1),c({title:"已刷新",description:"已从 bot_config.toml 重新读取配置"})},Xe=async()=>{try{l(!0),ie();const e=await Oe(Re());if(!e.success){c({title:"保存失败",description:e.error,variant:"destructive"}),l(!1);return}a(!1),c({title:"保存成功",description:"配置已保存,即将重启麦麦..."}),await new Promise(o=>setTimeout(o,$s)),await Ee()}catch(e){console.error("保存失败:",e),c({title:"保存失败",description:e.message,variant:"destructive"})}finally{l(!1)}},Je=t.useMemo(()=>te?Fs(te):[],[te]),Ze=t.useMemo(()=>({bot:b,personality:j,chat:R,expression:I,emoji:V,visual:B,voice:H,message_receive:$,keyword_reaction:F,response_post_process:z,chinese_typo:q,response_splitter:G,log:U,debug:W,maim_message:K,telemetry:Y,webui:X,database:J,mcp:Z,plugin_runtime:Q,a_memorix:ee}),[b,j,R,I,V,B,H,$,F,z,q,G,U,W,K,Y,X,J,Z,Q,ee]),Qe=t.useCallback((e,o)=>{({bot:D,personality:O,chat:se,expression:ue,emoji:de,visual:me,voice:pe,message_receive:fe,keyword_reaction:ge,response_post_process:he,chinese_typo:xe,response_splitter:ve,log:be,debug:Ce,maim_message:ye,telemetry:we,webui:je,database:Se,mcp:_e,plugin_runtime:ke,a_memorix:Te})[e]?.(o)},[]);return h?s.jsx(Pe,{className:"h-full",children:s.jsx("div",{className:"space-y-4 sm:space-y-6 p-4 sm:p-6",children:s.jsx("div",{className:"flex items-center justify-center h-64",children:s.jsx("p",{className:"text-muted-foreground",children:"加载中..."})})})}):s.jsx(Pe,{className:"h-full min-w-0",scrollbars:"vertical",children:s.jsxs("div",{className:"max-w-full space-y-4 overflow-x-hidden p-4 sm:space-y-6 sm:p-6",children:[s.jsx("div",{className:"flex flex-col gap-3 sm:gap-4",children:s.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsx("h1",{className:"text-xl sm:text-2xl md:text-3xl font-bold",children:"麦麦设置"}),s.jsx("p",{className:"text-muted-foreground mt-1 text-xs sm:text-sm",children:"管理麦麦的核心功能和行为设置"})]}),s.jsxs("div",{className:"flex w-full min-w-0 flex-wrap gap-2 sm:w-auto sm:flex-shrink-0 sm:justify-end",children:[s.jsx(He,{value:v,onValueChange:e=>Ue(e),className:"w-full min-w-0 sm:w-[14rem]",children:s.jsxs($e,{className:"grid h-8 w-full grid-cols-2 sm:h-9",children:[s.jsxs(ce,{value:"visual",className:"px-2 text-xs",children:[s.jsx(Ps,{className:"mr-1 h-3 w-3 sm:h-4 sm:w-4"}),"可视化"]}),s.jsxs(ce,{value:"source",className:"px-2 text-xs",children:[s.jsx(Ls,{className:"mr-1 h-3 w-3 sm:h-4 sm:w-4"}),"源代码"]})]})}),s.jsxs(P,{onClick:Ye,disabled:d||m||M,size:"sm",variant:"outline",className:"min-w-0 flex-1 sm:w-24 sm:flex-none",children:[s.jsx(Ds,{className:"h-3 w-3 sm:h-4 sm:w-4 mr-1"}),"刷新"]}),s.jsxs(P,{onClick:v==="visual"?We:Ge,disabled:d||m||!n||M,size:"sm",variant:"outline",className:"min-w-0 flex-1 sm:w-24 sm:flex-none",children:[s.jsx(Os,{className:"h-4 w-4 flex-shrink-0",strokeWidth:2,fill:"none"}),s.jsx("span",{className:"ml-1 truncate text-xs sm:text-sm",children:d?"保存中":m?"自动":n?"保存":"已保存"})]}),s.jsxs(ns,{children:[s.jsx(os,{asChild:!0,children:s.jsxs(P,{disabled:d||m||M,size:"sm",className:"min-w-0 flex-1 sm:w-28 sm:flex-none",children:[s.jsx(Is,{className:"h-4 w-4 flex-shrink-0"}),s.jsx("span",{className:"ml-1 truncate text-xs sm:text-sm",children:M?"重启中":n?"保存重启":"重启"})]})}),s.jsxs(is,{children:[s.jsxs(cs,{children:[s.jsx(ls,{children:"确认重启麦麦?"}),s.jsx(us,{asChild:!0,children:s.jsx("div",{children:s.jsx("p",{children:n?"当前有未保存的配置更改。点击确认将先保存配置,然后重启麦麦使新配置生效。重启过程中麦麦将暂时离线。":"即将重启麦麦主程序。重启过程中麦麦将暂时离线,配置将在重启后生效。"})})})]}),s.jsxs(ds,{children:[s.jsx(ms,{children:"取消"}),s.jsx(ps,{onClick:n?Xe:Ee,children:n?"保存并重启":"确认重启"})]})]})]})]})]})}),ae&&s.jsxs(Le,{children:[s.jsx(Ve,{className:"h-4 w-4"}),s.jsxs(De,{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[s.jsxs("span",{children:["配置更新后需要",s.jsx("strong",{children:"重启麦麦"}),'才能生效。你可以点击右上角的"保存并重启"按钮一键完成保存和重启。']}),s.jsx(P,{type:"button",variant:"outline",size:"sm",onClick:Ke,children:"我知道了"})]})]}),v==="source"&&s.jsxs("div",{className:"space-y-4",children:[s.jsxs(Le,{children:[s.jsx(Ve,{className:"h-4 w-4"}),s.jsxs(De,{children:[s.jsx("strong",{children:"源代码模式(高级功能):"}),"直接编辑 TOML 配置文件。此功能仅适用于熟悉 TOML 语法的高级用户。保存时会在前端验证格式,只有格式完全正确才能保存。",y&&w&&s.jsxs("div",{className:"text-destructive font-semibold mt-3 p-3 bg-destructive/10 rounded-md",children:[s.jsx("div",{className:"font-bold mb-2",children:"⚠️ TOML 格式错误:"}),s.jsx("pre",{className:"text-sm font-mono whitespace-pre-wrap break-words",children:w})]})]})]}),s.jsx(xs,{value:T,onChange:e=>{E(e),a(!0),y&&(C(!1),N(""))},language:"toml",height:"calc(100vh - 280px)",minHeight:"500px",placeholder:"TOML 配置内容"})]}),v==="visual"&&s.jsx(qs,{configSchema:te,tabGroups:Je,sectionValues:Ze,setSectionValue:Qe,setHasUnsavedChanges:a}),s.jsx(As,{})]})})}function Fe(h,x,d){const l=h&&typeof h=="object"&&!Array.isArray(h)?h:{},[m,...r]=x;return m?r.length===0?{...l,[m]:d}:{...l,[m]:Fe(l[m],r,d)}:l}function qs(h){const{configSchema:x,sectionValues:d,setHasUnsavedChanges:l,setSectionValue:m,tabGroups:r}=h,[n,a]=t.useState(!1),[v,k]=t.useState(r[0]?.id??""),[T,E]=t.useState(!1);if(t.useEffect(()=>{r.some(i=>i.id===v)||k(r[0]?.id??"")},[v,r]),r.length===0||!x?.nested)return null;const y=n?r:r.filter(i=>A.has(i.id)),C=r.some(i=>!A.has(i.id)),w=y.find(i=>!A.has(i.id))?.id,N=()=>{a(i=>{if(i&&!A.has(v)){const c=r.find(L=>A.has(L.id));k(c?.id??r[0]?.id??"")}return!i})},ae=i=>{const c=i.sections.map(b=>[b,x.nested?.[b]]).filter(b=>!!b[1]);if(c.length===0)return null;const L=Object.fromEntries(c.map(([b])=>[b,d[b]??{}])),M={className:i.id,classDoc:i.label,fields:[],nested:Object.fromEntries(c)};return s.jsx(Rs,{schema:M,values:L,onChange:(b,D)=>{const[j,...O]=b.split(".");if(!j)return;const R=d[j]??{},se=O.length===0?D:Fe(R,O,D);m(j,se),l(!0)},hooks:le,advancedVisible:T,sectionColumns:2})};return s.jsxs(He,{value:v,onValueChange:k,className:"w-full",children:[s.jsxs($e,{className:"flex h-auto max-w-full justify-start gap-1 overflow-x-auto p-1 transition-all duration-300 ease-out sm:flex-wrap sm:overflow-x-visible",children:[y.map(i=>{const c=!A.has(i.id);return s.jsxs(t.Fragment,{children:[i.id===w&&s.jsx("span",{className:"mx-1 hidden h-6 w-px bg-border/80 transition-opacity duration-200 sm:block"}),s.jsx(ce,{value:i.id,className:gs("shrink-0 px-2 py-1.5 text-sm transition-all duration-200 ease-out sm:px-3 sm:py-2 data-[state=active]:shadow-sm",c&&"border border-dashed border-border/70 bg-background/45 text-muted-foreground/80 motion-safe:animate-[config-tab-enter_180ms_ease-out_both] hover:bg-background/70 data-[state=active]:border-primary/45 data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:shadow-none"),children:i.label})]},i.id)}),C&&s.jsxs(P,{type:"button",variant:"ghost",size:"sm",className:"group h-8 shrink-0 px-2 text-xs transition-all duration-200 ease-out sm:h-9 sm:px-3",onClick:N,children:[n?s.jsx(Vs,{className:"mr-1 h-3.5 w-3.5 transition-transform duration-200 group-hover:-translate-x-0.5"}):s.jsx(Bs,{className:"mr-1 h-3.5 w-3.5 transition-transform duration-200 group-hover:translate-x-0.5"}),n?"收起":"更多"]}),s.jsx(P,{type:"button",variant:T?"default":"outline",size:"sm",className:"h-8 shrink-0 px-2 text-xs transition-all duration-200 ease-out sm:ml-auto sm:h-9 sm:px-3",onClick:()=>E(i=>!i),children:"高级设置"})]}),r.map(i=>s.jsx(hs,{value:i.id,className:"space-y-4 motion-safe:animate-[config-tab-content-enter_180ms_ease-out_both]",children:ae(i)},i.id))]})}export{dt as BotConfigPage};
|