openlearn-next 0.7.13 → 0.7.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli-data.mjs +2 -2
- package/cli-doctor.mjs +2 -1
- package/dist/assets/{AcademicYearFilter-Dm3no4Nb.js → AcademicYearFilter-pCzZgimU.js} +1 -1
- package/dist/assets/ActivityWorkspaceWidget-KJQdLnvf.js +1 -0
- package/dist/assets/AdminDirectoryView-DDxX8RO2.js +5 -0
- package/dist/assets/AssessmentCenter-D5cVUqvy.js +1 -0
- package/dist/assets/{AssignmentFilePreview-7fHtUuUL.js → AssignmentFilePreview-DK29gdpQ.js} +2 -2
- package/dist/assets/BatchPickerModal-Cw4OZqOJ.js +1 -0
- package/dist/assets/ClassesView-Czx7hU2V.js +32 -0
- package/dist/assets/ClassroomCountdownWidget-BeAM37Cq.js +2 -0
- package/dist/assets/CloudDriveModal-GNZzWVCP.js +1 -0
- package/dist/assets/ComputerLabView-C_0c7sjz.js +1 -0
- package/dist/assets/CourseManagement-DzLB0Xmp.js +1 -0
- package/dist/assets/CourseWizardModal-DmQu237c.js +3 -0
- package/dist/assets/CoursewareHubPanel-Xn5WbXg9.js +1 -0
- package/dist/assets/Dashboard-CX83FUeF.js +8 -0
- package/dist/assets/ExportWeightModal-C0SuUhHM.js +1 -0
- package/dist/assets/HelpTour-D8hVFA6N.js +1 -0
- package/dist/assets/HelpView-C0PP_51D.js +204 -0
- package/dist/assets/ImportLessonsModal-p8Uc1U_h.js +1 -0
- package/dist/assets/{ImportModal-CYU3aMYY.js → ImportModal-DNaPHV0I.js} +2 -2
- package/dist/assets/InteractiveCoursewareViewer-D9YTgasv.js +1 -0
- package/dist/assets/InteractiveWhiteboard-CHYQfRjR.js +19 -0
- package/dist/assets/LazyCourseware-dtuOSiyR.js +2 -0
- package/dist/assets/LazyWhiteboard-DuBKAh4-.js +2 -0
- package/dist/assets/LessonEditorView-BgI7C_FK.js +1 -0
- package/dist/assets/LiveClassroomView-CJK_WMqD.js +67 -0
- package/dist/assets/NotificationDetailModal-BVYuS4uR.js +1 -0
- package/dist/assets/PluginView-CpWjy0Xf.js +66 -0
- package/dist/assets/ProcessLogsModal-D-Ez_YZA.js +1 -0
- package/dist/assets/QuizGeneratorModal-Lf5QMvkq.js +1 -0
- package/dist/assets/RevealPresentationWrapper-Dnf4v72i.js +48 -0
- package/dist/assets/StudentAssignmentEvalPanel-C-7YSfEQ.js +1 -0
- package/dist/assets/StudentAssignmentView-LuMaNpq8.js +1 -0
- package/dist/assets/StudentLessonView-DUG8I1Zy.js +1 -0
- package/dist/assets/StudentView-mMwGPiiF.js +2 -0
- package/dist/assets/SystemResourceLibraryModal-LUDNOwgW.js +1 -0
- package/dist/assets/TeacherView-ofB-3SEJ.js +2 -0
- package/dist/assets/TimetableView-DrZbyCOQ.js +6 -0
- package/dist/assets/{UTIF-u3Ct70K4.js → UTIF-DbcUtUKZ.js} +1 -1
- package/dist/assets/index-CG-g_9my.css +1 -0
- package/dist/assets/index-CLtQGl25.js +169 -0
- package/dist/assets/{index.es-CcQ2kcDj.js → index.es-D1mLdmCj.js} +1 -1
- package/dist/assets/keyboard-Rs007iOv.js +1 -0
- package/dist/assets/{pluginParsers-BN3lQ7PJ.js → pluginParsers-C46aCYkl.js} +1 -1
- package/dist/assets/student-workspace-BFPfR77J.js +1 -0
- package/dist/assets/{vendor-charts-skggaP9D.js → vendor-charts-CK-CU_m7.js} +1 -1
- package/dist/assets/{vendor-icons-CqLdl4B_.js → vendor-icons-__JLt-CP.js} +2 -2
- package/dist/assets/{vendor-konva-BOuJWFuE.js → vendor-konva-DGELdczq.js} +1 -1
- package/dist/assets/{vendor-motion-BFAPOs-b.js → vendor-motion-DtyD8eRQ.js} +1 -1
- package/dist/assets/{vendor-pdf-BMEc_Clb.js → vendor-pdf-DNexfcCO.js} +3 -3
- package/dist/assets/{vendor-pptx-C0qaOH5T.js → vendor-pptx-ByQkJ0Xi.js} +4 -4
- package/dist/assets/{vendor-react-DHoBZpn2.js → vendor-react-lfWn1oGs.js} +1 -1
- package/dist/assets/{vendor-utils-BxOuk3id.js → vendor-utils-CsoI4M9x.js} +1 -1
- package/dist/cli-migrator.cjs +23 -7
- package/dist/index.html +10 -6
- package/dist/server.cjs +11139 -4094
- package/docs_plugin_guide.md +1023 -0
- package/migrations/046_logout_clear_site_data.sql +9 -0
- package/migrations/047_third_party_theme_system.sql +55 -0
- package/migrations/048_file_storage_extension.sql +63 -0
- package/migrations/049_ai_personas.sql +25 -0
- package/migrations/050_ai_persona_governance.sql +11 -0
- package/migrations/051_community_registry_url.sql +9 -0
- package/migrations/052_community_install_enabled.sql +12 -0
- package/migrations/053_runtime_admin_policies.sql +28 -0
- package/migrations/054_user_onboarding_tour_status.sql +10 -0
- package/migrations/055_classroom_collab_whiteboard.sql +27 -0
- package/migrations/056_public_student_lookup.sql +9 -0
- package/migrations/README.md +59 -48
- package/migrations/schema-golden.json +98 -2
- package/package.json +7 -6
- package/scripts/assert-node-runtime.mjs +4 -3
- package/dist/assets/ActivityWorkspaceWidget-9DeXkxGk.js +0 -1
- package/dist/assets/AdminDirectoryView-5gupeGAR.js +0 -5
- package/dist/assets/AssessmentCenter-CPaqy0Xa.js +0 -1
- package/dist/assets/BatchPickerModal-0yIe1DXi.js +0 -1
- package/dist/assets/ClassesView-CGpoW0IC.js +0 -32
- package/dist/assets/ClassroomCountdownWidget-B_5--dn_.js +0 -2
- package/dist/assets/CloudDriveModal-4Ts5Duh5.js +0 -1
- package/dist/assets/ComputerLabView-DAEaKTBj.js +0 -1
- package/dist/assets/CourseManagement-CxIBH7lA.js +0 -1
- package/dist/assets/CourseWizardModal-R0xpa_Aw.js +0 -3
- package/dist/assets/CoursewareHubPanel-CpHptmSa.js +0 -1
- package/dist/assets/Dashboard-DeBfP_Nc.js +0 -8
- package/dist/assets/ExportWeightModal-DlnCOBlL.js +0 -1
- package/dist/assets/HelpTour-Cztpqw_J.js +0 -1
- package/dist/assets/HelpView-LPLAY9bq.js +0 -204
- package/dist/assets/ImportLessonsModal-BopEHmU_.js +0 -1
- package/dist/assets/InteractiveCoursewareViewer-CGc3P4HF.js +0 -1
- package/dist/assets/InteractiveWhiteboard-DiyHrIGn.js +0 -19
- package/dist/assets/LazyCourseware-Bj1jm0nC.js +0 -2
- package/dist/assets/LazyWhiteboard-NPD1EoyO.js +0 -2
- package/dist/assets/LessonEditorView-DqreBtFx.js +0 -1
- package/dist/assets/LiveClassroomView-eTc6Fb9g.js +0 -64
- package/dist/assets/NotificationDetailModal-B7G7ra9a.js +0 -1
- package/dist/assets/PluginView-DP6s-L70.js +0 -77
- package/dist/assets/ProcessLogsModal-Dq5C4s7j.js +0 -1
- package/dist/assets/QuizGeneratorModal-CoX9f8Wp.js +0 -1
- package/dist/assets/RevealPresentationWrapper-wwlcfNWB.js +0 -48
- package/dist/assets/StudentAssignmentEvalPanel-DHb-asHQ.js +0 -1
- package/dist/assets/StudentAssignmentView-AJpjQK3f.js +0 -1
- package/dist/assets/StudentLessonView-Djd1T1QF.js +0 -1
- package/dist/assets/StudentView-D41z7GY5.js +0 -2
- package/dist/assets/SystemResourceLibraryModal-CxmQh1HA.js +0 -1
- package/dist/assets/TeacherView-DqSWaNuI.js +0 -2
- package/dist/assets/TimetableView-DrFs_xgc.js +0 -6
- package/dist/assets/index-7CK1B0tE.js +0 -131
- package/dist/assets/index-BaGZmpr6.css +0 -1
- package/dist/assets/student-workspace-DkTkcAGK.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e}from"./vendor-react-lfWn1oGs.js";import{k as n}from"./index-CLtQGl25.js";import{aa as o,k as d,a7 as l,a_ as i}from"./vendor-icons-__JLt-CP.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";function u({notification:s,onClose:t,lang:r,onOpenWorkspace:a}){return s?e.jsxs(n,{open:!!s,onClose:t,ariaLabelledBy:"notification-detail-dialog-title",closedBy:"any",className:"bg-white border text-gray-900 border-gray-200 rounded-2xl shadow-2xl max-w-xl overflow-hidden max-h-[85vh] font-sans",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 flex items-center justify-between bg-gray-50/80 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(o,{className:"text-indigo-600 font-sans shrink-0",size:20}),e.jsx("h2",{id:"notification-detail-dialog-title",className:"font-bold text-gray-800 text-base font-sans truncate",children:s.title})]}),e.jsx("button",{type:"button",onClick:t,"aria-label":r==="zh"?"关闭弹窗":"Close dialog",className:"text-gray-400 hover:text-gray-600 font-bold p-1 hover:bg-gray-200 rounded transition-colors text-lg cursor-pointer",children:"×"})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-4",children:[e.jsx("div",{className:"p-4 bg-indigo-50/50 rounded-xl border border-indigo-100 text-sm text-gray-700 leading-relaxed font-sans",children:s.message}),s.assignment&&e.jsxs("div",{className:"space-y-4 font-sans",children:[e.jsxs("div",{className:"border border-gray-150 rounded-xl p-4 bg-gray-50/50 space-y-3",children:[e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsx("span",{className:"text-xs text-gray-400 uppercase tracking-wider font-semibold font-sans",children:r==="zh"?"关联作业":"Associated Assignment"}),s.assignment.submission_status==="graded"&&e.jsx("span",{className:"bg-green-100 border border-green-200 text-green-800 text-xs font-bold px-2.5 py-1 rounded-full shadow-sm font-mono",children:r==="zh"?`得分:${s.assignment.score}%`:`Score: ${s.assignment.score}%`})]}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-bold text-base text-indigo-900 font-sans",children:s.assignment.title}),e.jsx("p",{className:"text-xs text-gray-500 mt-1 font-sans",children:s.assignment.class_name})]}),s.assignment.feedback&&e.jsxs("div",{className:"mt-3 bg-white p-3 rounded-lg border border-gray-150 space-y-1 font-sans",children:[e.jsxs("div",{className:"text-xs font-bold text-green-700 flex items-center justify-between gap-2 font-sans",children:[e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(d,{size:14,className:"font-sans"}),e.jsx("span",{children:r==="zh"?"教师评审意见与反馈":"Teacher Feedback & Recommendations"})]}),s.assignment.graded_at&&e.jsxs("div",{className:"text-xs text-gray-400 font-mono flex items-center gap-1 font-normal select-none",children:[e.jsx(l,{size:11,className:"text-neutral-400"}),e.jsx("span",{children:new Date(s.assignment.graded_at).toLocaleString(r==="zh"?"zh-CN":"en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})})]})]}),e.jsx("div",{className:"text-xs text-gray-600 italic bg-green-50/30 p-2 rounded border border-green-50 mt-1 leading-relaxed whitespace-pre-wrap font-sans",children:s.assignment.feedback})]})]}),s.assignment.submission_content&&e.jsxs("div",{className:"space-y-1.5 font-sans",children:[e.jsx("div",{className:"text-xs text-gray-400 font-bold uppercase tracking-wider font-sans",children:r==="zh"?"我提交的内容":"My Submission Content"}),e.jsx("div",{className:"bg-gray-900 text-gray-100 p-4 rounded-xl border border-gray-800 overflow-x-auto max-h-40 overflow-y-auto text-xs font-mono leading-relaxed whitespace-pre-wrap",children:s.assignment.submission_content})]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-100 bg-gray-50/85 flex justify-end gap-2.5 shrink-0 font-sans",children:[e.jsx("button",{type:"button",onClick:t,className:"px-4 py-2 text-xs font-semibold border border-gray-200 text-gray-700 bg-white rounded-lg hover:bg-gray-100 transition-colors cursor-pointer",children:r==="zh"?"关闭":"Close"}),s.assignment&&e.jsxs("button",{type:"button",onClick:()=>a(s.assignment),className:"px-4 py-2 text-xs font-bold bg-indigo-600 text-white border border-indigo-700 rounded-lg hover:bg-indigo-700 hover:shadow shadow-sm transition-all cursor-pointer flex items-center gap-1.5 font-sans",children:[e.jsx(i,{size:14,className:"font-sans"}),r==="zh"?"打开画布 / 查看详情":"Open Workspace Canvas"]})]})]}):null}export{u as NotificationDetailModal};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import{r as o,j as e,R as be}from"./vendor-react-lfWn1oGs.js";import{f as K,av as Ut,aB as St,ah as Be,T as Pe,ar as Pt,b$ as Et,bB as Ze,X as Se,aj as Rt,b as ct,_ as xt,b1 as Tt,aM as Dt,o as ut,ab as Lt,aA as At,U as Ot,c0 as mt,aN as Mt,R as Fe,c1 as _t,O as Ft,c2 as zt,af as Ke,c3 as ot,S as Gt,K as ht,k as Ge,b6 as Bt,c4 as Vt,a7 as Ht,bv as Ct,Q as Qe,n as it,x as pt,e as st,aE as Jt,P as It,aw as Xe,ai as gt,c5 as Zt,aF as qt}from"./vendor-icons-__JLt-CP.js";import{ad as Wt,e as bt,s as ze,d as Yt}from"./index-CLtQGl25.js";import{p as Kt}from"./pluginParsers-C46aCYkl.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";function Qt({pluginId:s,lang:t}){const r=t==="zh",i=(l,j)=>r?l:j,[c,x]=o.useState(null),[n,g]=o.useState(!0),[T,b]=o.useState(!1),[O,D]=o.useState(null),[G,v]=o.useState(null),I=`/api/plugins/${encodeURIComponent(s)}/network-grants`,R=o.useCallback(async()=>{g(!0);try{const l=await fetch(I);if(l.status===403||l.status===404){b(!0);return}const j=await l.json().catch(()=>({}));if(!l.ok||j?.success===!1){b(!0);return}x({proposed:Array.isArray(j.proposed)?j.proposed:[],approved:Array.isArray(j.approved)?j.approved:[],rejected:Array.isArray(j.rejected)?j.rejected:[]}),b(!1)}catch{b(!0)}finally{g(!1)}},[I]);o.useEffect(()=>{R()},[R]);const y=o.useCallback(async l=>{D(l),v(null);try{const j=await fetch(I,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hosts:[l]})}),k=await j.json().catch(()=>({}));(!j.ok||k?.success!==!0)&&v(k?.error||i("批准失败","Approval failed"))}catch(j){v(j?.message||i("批准失败","Approval failed"))}finally{D(null),await R()}},[I,R,i]),N=o.useCallback(async l=>{D(l),v(null);try{const j=await fetch(`${I}?host=${encodeURIComponent(l)}`,{method:"DELETE"}),k=await j.json().catch(()=>({}));(!j.ok||k?.success!==!0)&&v(k?.error||i("撤销失败","Revoke failed"))}catch(j){v(j?.message||i("撤销失败","Revoke failed"))}finally{D(null),await R()}},[I,R,i]),E=c&&(c.approved.length>0||c.proposed.length>0||c.rejected.length>0);if(T||!n&&!E)return null;if(n)return e.jsxs("div",{className:"py-4 flex items-center gap-2 text-xs text-gray-400","data-testid":"network-grants-loading",children:[e.jsx(K,{size:14,className:"animate-spin"}),i("正在读取出站域名授权…","Loading outbound grants…")]});const m=(c?.proposed??[]).filter(l=>!(c?.approved??[]).includes(l));return e.jsxs("div",{className:"pt-4 mt-2 border-t border-gray-100","data-testid":"network-grants-panel",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(Ut,{size:15,className:"text-gray-500"}),e.jsx("h4",{className:"text-sm font-medium text-gray-700",children:i("出站网络域名","Outbound network hosts")}),e.jsxs("span",{className:"text-[10px] font-mono text-gray-400 bg-gray-50 border border-gray-200 rounded px-1.5 py-0.5",children:[c?.approved.length??0," ",i("已批准","approved")]})]}),e.jsx("p",{className:"text-xs text-gray-400 mb-3 leading-relaxed",children:i("清单声明的域名只是申请,只有「已批准」的才会被运行时放行。回环、私有与保留地址永远不会被批准。","Hosts declared in the manifest are requests only; the runtime allows just the approved ones. Loopback, private and reserved addresses are never granted.")}),G&&e.jsx("div",{className:"mb-2 text-xs text-rose-600 bg-rose-50 border border-rose-100 rounded-lg px-2.5 py-1.5",role:"alert",children:G}),e.jsxs("div",{className:"mb-3",children:[e.jsxs("div",{className:"text-[11px] font-bold text-emerald-700 mb-1.5 flex items-center gap-1",children:[e.jsx(St,{size:12}),i("已批准(运行时放行)","Approved (allowed at runtime)")]}),c&&c.approved.length>0?e.jsx("ul",{className:"space-y-1",children:c.approved.map(l=>e.jsxs("li",{className:"flex items-center justify-between gap-2 bg-emerald-50/60 border border-emerald-100 rounded-lg px-2.5 py-1.5",children:[e.jsx("code",{className:"text-xs text-emerald-900 font-mono truncate",children:l}),e.jsxs("button",{type:"button",onClick:()=>void N(l),disabled:O===l,"data-testid":`revoke-${l}`,className:"text-[11px] font-bold text-rose-600 hover:text-rose-700 flex items-center gap-1 disabled:opacity-40 cursor-pointer",children:[O===l?e.jsx(K,{size:11,className:"animate-spin"}):e.jsx(Be,{size:11}),i("撤销","Revoke")]})]},l))}):e.jsx("p",{className:"text-xs text-gray-400 italic","data-testid":"no-approved",children:i("尚未批准任何域名 —— 该插件无法发起出站请求。","No host approved yet — this plugin cannot make outbound requests.")})]}),m.length>0&&e.jsxs("div",{className:"mb-3",children:[e.jsxs("div",{className:"text-[11px] font-bold text-amber-700 mb-1.5 flex items-center gap-1",children:[e.jsx(Pe,{size:12}),i("清单申请中(未生效)","Requested by manifest (not active)")]}),e.jsx("ul",{className:"space-y-1",children:m.map(l=>e.jsxs("li",{className:"flex items-center justify-between gap-2 bg-amber-50/60 border border-amber-100 rounded-lg px-2.5 py-1.5",children:[e.jsx("code",{className:"text-xs text-amber-900 font-mono truncate",children:l}),e.jsxs("button",{type:"button",onClick:()=>void y(l),disabled:O===l,"data-testid":`approve-${l}`,className:"text-[11px] font-bold text-indigo-600 hover:text-indigo-700 flex items-center gap-1 disabled:opacity-40 cursor-pointer",children:[O===l?e.jsx(K,{size:11,className:"animate-spin"}):e.jsx(Pt,{size:11}),i("批准","Approve")]})]},l))})]}),c&&c.rejected.length>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"text-[11px] font-bold text-rose-700 mb-1.5 flex items-center gap-1",children:[e.jsx(Et,{size:12}),i("不可批准","Not approvable")]}),e.jsx("ul",{className:"space-y-1","data-testid":"rejected-hosts",children:c.rejected.map(l=>e.jsxs("li",{className:"text-xs text-gray-500 leading-relaxed",children:[e.jsx("code",{className:"font-mono text-rose-700 line-through",children:l.host}),l.reason?e.jsxs("span",{className:"ml-1.5 text-gray-400",children:["— ",l.reason]}):null]},`${l.host}-${l.reason??""}`))})]})]})}function Xt({pluginId:s,pluginName:t,manifestStr:r,lang:i,onClose:c}){const[x,n]=o.useState(null),[g,T]=o.useState({}),[b,O]=o.useState(!0),[D,G]=o.useState(!1),[v,I]=o.useState(null),R=o.useCallback(async()=>{O(!0);try{const j=await fetch(`/api/plugins/${encodeURIComponent(s)}/config`);if(j.ok){const k=await j.json();if(k.success){n(k.result.schema),T(k.result.values);return}}}catch{}try{const k=JSON.parse(r).configuration?.properties??{};n(k);const M={};for(const[p,f]of Object.entries(k))M[p]=f.default;T(M)}catch{n({})}O(!1)},[s,r]);o.useEffect(()=>{R().finally(()=>O(!1))},[R]);const y=async()=>{G(!0),I(null);try{const k=await(await fetch(`/api/plugins/${encodeURIComponent(s)}/config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(g)})).json();k.success?I(i==="zh"?"✓ 已保存":"✓ Saved"):I(`✗ ${k.error}`)}catch(j){I(`✗ ${j.message}`)}G(!1)},N=(j,k)=>{T(M=>({...M,[j]:k})),I(null)},E=(j,k)=>i==="zh"?j:k,m=(j,k)=>{const M=g[j],p=k.description??"";switch(k.type){case"boolean":return e.jsxs("div",{className:"flex items-center justify-between py-2.5 border-b border-gray-100",children:[e.jsxs("div",{className:"flex-1",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700",children:j}),p&&e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:p})]}),e.jsx("button",{type:"button",onClick:()=>N(j,!M),className:`relative w-10 h-5 rounded-full transition-colors ${M?"bg-indigo-600":"bg-gray-300"}`,children:e.jsx("span",{className:`absolute top-0.5 w-4 h-4 rounded-full bg-white shadow transition-transform ${M?"translate-x-5":"translate-x-0.5"}`})})]},j);case"number":case"integer":return e.jsxs("div",{className:"py-2.5 border-b border-gray-100",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700 block mb-1",children:j}),p&&e.jsx("p",{className:"text-xs text-gray-400 mb-1.5",children:p}),e.jsx("input",{type:"number",step:k.type==="integer"?1:"any",min:k.minimum,max:k.maximum,value:M!=null?String(M):"",onChange:f=>{const F=f.target.value===""?void 0:Number(f.target.value);N(j,F)},className:"w-full px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-400 outline-none",placeholder:k.default!==void 0?String(k.default):""})]},j);default:return k.enum&&k.enum.length>0?e.jsxs("div",{className:"py-2.5 border-b border-gray-100",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700 block mb-1",children:j}),p&&e.jsx("p",{className:"text-xs text-gray-400 mb-1.5",children:p}),e.jsx("select",{value:String(M??k.default??""),onChange:f=>N(j,f.target.value),className:"w-full px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-400 outline-none bg-white",children:k.enum.map(f=>e.jsx("option",{value:String(f),children:String(f)},String(f)))})]},j):e.jsxs("div",{className:"py-2.5 border-b border-gray-100",children:[e.jsx("label",{className:"text-sm font-medium text-gray-700 block mb-1",children:j}),p&&e.jsx("p",{className:"text-xs text-gray-400 mb-1.5",children:p}),e.jsx("input",{type:"text",value:M!=null?String(M):"",onChange:f=>N(j,f.target.value),className:"w-full px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-200 focus:border-indigo-400 outline-none",placeholder:k.default!==void 0?String(k.default):""})]},j)}},l=x&&Object.keys(x).length>0;return e.jsx("div",{className:"fixed inset-0 bg-black/40 flex items-center justify-center z-50 p-4",onClick:c,children:e.jsxs("div",{className:"bg-white rounded-xl shadow-xl w-full max-w-md flex flex-col max-h-[85vh]",onClick:j=>j.stopPropagation(),children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Ze,{size:18,className:"text-gray-500"}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-gray-800 text-sm",children:E("插件设置","Plugin Settings")}),e.jsx("p",{className:"text-xs text-gray-400",children:t})]})]}),e.jsx("button",{onClick:c,className:"text-gray-400 hover:text-gray-600 p-1",children:e.jsx(Se,{size:18})})]}),e.jsxs("div",{className:"p-4 flex-1 overflow-auto",children:[b?e.jsx("div",{className:"flex items-center justify-center py-12",children:e.jsx(K,{size:24,className:"text-gray-400 animate-spin"})}):l?e.jsx("div",{className:"space-y-0.5",children:Object.entries(x).map(([j,k])=>m(j,k))}):e.jsxs("div",{className:"text-center py-8",children:[e.jsx(Ze,{size:32,className:"text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-sm text-gray-400",children:E("此插件没有可配置项。","This plugin has no configurable settings.")}),e.jsx("p",{className:"text-xs text-gray-300 mt-1",children:E("在 manifest.configuration 中声明配置项即可在此处显示。","Declare settings in manifest.configuration to see them here.")})]}),e.jsx(Qt,{pluginId:s,lang:i})]}),l&&e.jsxs("div",{className:"p-4 border-t border-gray-100 flex items-center justify-between",children:[v&&e.jsx("span",{className:`text-xs ${v.startsWith("✓")?"text-green-600":"text-red-500"}`,children:v}),e.jsxs("div",{className:"flex gap-2 ml-auto",children:[e.jsx("button",{onClick:c,className:"px-4 py-2 text-xs font-medium text-gray-600 hover:bg-gray-50 rounded-lg transition-colors",children:E("取消","Cancel")}),e.jsxs("button",{onClick:y,disabled:D,className:"px-4 py-2 text-xs font-medium bg-indigo-600 text-white hover:bg-indigo-700 rounded-lg transition-colors flex items-center gap-1.5 disabled:opacity-50",children:[D?e.jsx(K,{size:14,className:"animate-spin"}):e.jsx(Rt,{size:14}),E("保存","Save")]})]})]})]})})}function es(s){const t=[];for(const r of s){if(typeof r!="string"){t.push({raw:String(r),host:null,approvable:!1,reason:"不是字符串"});continue}const i=r.trim(),c=ts(i);if(c){t.push({raw:i,host:c,approvable:!0});continue}let x="不是合法的域名或 IP";i?i.length>253?x="域名过长(>253)":i.includes("*")?x="不支持通配符,请逐个列出域名":i.includes("/")||i.includes(":")||i.includes(" ")?x="只能写主机名,不能带协议、端口或路径":$t(i)?x="回环/私有/保留地址一律拒绝(SSRF 防线)":/^\d+\.\d+\.\d+\.\d+$/.test(i)&&(x="非法 IPv4 字面量"):x="空字符串",t.push({raw:i,host:null,approvable:!1,reason:x})}return t}function ft(s){const[t,r,i]=s;return t===0||t===10||t===127||t===100&&r>=64&&r<=127||t===169&&r===254||t===172&&r>=16&&r<=31||t===192&&r===0&&i===0||t===192&&r===0&&i===2||t===192&&r===168||t===198&&(r===18||r===19)||t===198&&r===51&&i===100||t===203&&r===0&&i===113||t>=224}function lt(s){if(/^(0[xX][0-9a-fA-F]+|\d+)$/.test(s)){let c;return/^0[xX]/.test(s)?c=parseInt(s.slice(2),16):/^0\d+$/.test(s)&&/^[0-7]+$/.test(s.slice(1))?c=parseInt(s.slice(1),8):c=Number(s),!Number.isSafeInteger(c)||c<0||c>=2**32?null:[c>>>24&255,c>>>16&255,c>>>8&255,c&255]}const t=s.split(".");if(t.length!==4)return null;const r=/^(0[xX][0-9a-fA-F]+|0[0-7]*|\d+)$/;if(!t.every(c=>r.test(c)))return null;const i=t.map(c=>{let x;return/^0[xX]/.test(c)?x=parseInt(c.slice(2),16):/^0\d/.test(c)?x=parseInt(c.slice(1),8):x=parseInt(c,10),Number.isSafeInteger(x)&&x>=0&&x<=255?x:-1});return i.some(c=>c<0)?null:i}function $t(s){const t=s.trim().toLowerCase().replace(/\.$/,"");if(!t||t==="localhost"||t.endsWith(".localhost")||t.endsWith(".local")||t==="0.0.0.0"||t==="::"||t==="::1"||t==="[::]"||t==="[::1]")return!0;const r=t.startsWith("[")&&t.endsWith("]")?t.slice(1,-1):t;if(r.includes(":")){if(r==="::"||r==="::1"||/^0*:0*:0*:0*:0*:0*:0*:0*1$/i.test(r)||/^0*:0*:0*:0*:0*:0*:0*:0*$/i.test(r)||/^f[cd][0-9a-f]{2}:/i.test(r)||/^fe[89ab][0-9a-f]:/i.test(r)||/^ff[0-9a-f]{2}:/i.test(r))return!0;const c=/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i.exec(r);if(c){const x=lt(c[1]);return x?ft(x):!0}return!1}const i=lt(r);return i?ft(i):!1}function ts(s){if(typeof s!="string")return null;const t=s.trim().toLowerCase().replace(/\.$/,"");return!t||t.length>253||t.includes("/")||t.includes(":")||t.includes(" ")||t.includes("*")||$t(t)||/^\d+\.\d+\.\d+\.\d+$/.test(t)&&!lt(t)?null:t}function ss(s){return s==="inline"||s==="worker"||s==="process"?s:null}function rt(s){return s.replace(/\\u\{([0-9a-fA-F]+)\}/g,(t,r)=>{try{return String.fromCodePoint(parseInt(r,16))}catch{return t}}).replace(/\\u([0-9a-fA-F]{4})/g,(t,r)=>String.fromCharCode(parseInt(r,16))).replace(/\\x([0-9a-fA-F]{2})/g,(t,r)=>String.fromCharCode(parseInt(r,16))).replace(/\\n/g,`
|
|
2
|
+
`).replace(/\\t/g," ").replace(/\\r/g,"\r").replace(/\\'/g,"'").replace(/\\"/g,'"').replace(/\\\\/g,"\\")}function yt(s,t){if(!s)return t;for(const r of["label","name","title","displayName"]){const i=s[r];if(typeof i=="string"&&i.trim())return i.trim()}return t}function jt(s){if(!s||typeof s!="object")return[];const t=[],r=new Set,i=(x,n,g)=>{const T=`${x}::${n}`;r.has(T)||(r.add(T),t.push({slot:x,id:n,label:g||n||x,source:"manifest"}))},c=s.contributes;if(c&&typeof c=="object"){for(const[x,n]of Object.entries(c))if(Array.isArray(n))for(const g of n){const T=g&&typeof g=="object"?g:{},b=typeof T.id=="string"?T.id:"?";i(x,b,yt(T,b))}}if(Array.isArray(s.classroomTools))for(const x of s.classroomTools){const n=x&&typeof x=="object"?x:{},g=typeof n.id=="string"?n.id:"?";i("classroom.tool",g,yt(n,g))}return t}function rs(s){if(!s)return[];const t=[],r=/registerExtensionPoint\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{((?:[^{}]|\{[^{}]*\})*)\}/g;let i;for(;(i=r.exec(s))!==null;){const c=rt(i[1]),x=i[2],n=x.match(/\bid\s*:\s*['"]([^'"]*)['"]/),g=x.match(/\blabel\s*:\s*['"]([^'"]*)['"]/),T=x.match(/\bname\s*:\s*['"]([^'"]*)['"]/),b=rt(n?.[1]||"?"),O=g?.[1]??T?.[1]??b;t.push({slot:c,id:b,label:rt(O)||b||c,source:"frontend"})}return t}function as(s,t){const r=new Map;for(const i of s)r.set(`${i.slot}::${i.id}`,i);for(const i of t){const c=`${i.slot}::${i.id}`,x=r.get(c);if(!x){r.set(c,i);continue}const n=x.label&&x.label!==x.id&&x.label!==x.slot?x.label:i.label;r.set(c,{...x,label:n||x.label})}return Array.from(r.values())}const ns=new Set(["teacher.tab","teacher.dashboard.widget","workspace.view","classroom.tool","command.palette"]),os=new Set(["student.view","student.dashboard.widget"]);function vt(s){if(!s||typeof s!="string")return"0.0.0";const t=s.trim().match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/);return t?`${t[1]}.${t[2]||"0"}.${t[3]||"0"}`:"0.0.0"}function is(s,t){const r=vt(s).split(".").map(c=>parseInt(c,10)),i=vt(t).split(".").map(c=>parseInt(c,10));for(let c=0;c<3;c++){if(r[c]<i[c])return-1;if(r[c]>i[c])return 1}return 0}function ls({isOpen:s,onClose:t,lang:r,file:i,onConfirmInstall:c,lockedTargetPluginId:x=null,installedPlugins:n=[]}){const[g,T]=o.useState(0),[b,O]=o.useState(null),[D,G]=o.useState([]),[v,I]=o.useState([]),[R,y]=o.useState(null),[N,E]=o.useState(!1),[m,l]=o.useState(!1),[j,k]=o.useState("process"),[M,p]=o.useState("unknown"),[f,F]=o.useState(!1),[Q,de]=o.useState(!1),[Ce,U]=o.useState(!1),[ee,ce]=o.useState(null),[fe,se]=o.useState(null),[Ee,ue]=o.useState(!1),[ve,ae]=o.useState(!1),[te,he]=o.useState(""),[ye,xe]=o.useState(0),[ne,Ie]=o.useState("teacher"),[Ne,$e]=o.useState([]),[Re,W]=o.useState(!1),[H,pe]=o.useState(null),[Z,we]=o.useState(!1),[ge,Y]=o.useState(!1),[Le,Te]=o.useState(!1),[Ae,Oe]=o.useState(!0),[oe,ie]=o.useState(!1);o.useEffect(()=>{s&&i&&(T(0),O(null),G([]),y(null),E(!1),l(!1),k("process"),p("unknown"),F(!1),ce(null),se(null),$e([]),I([]),Ie("teacher"),pe(null),we(!1),Y(!1),Te(!1),ie(!1),Oe(!0),S(i),z())},[s,i]);const S=async d=>{ue(!0),se(null);try{const $=await Wt.loadAsync(d);ce($),Te(!!$.file("signature.json"));const A=$.file("manifest.json");if(!A)throw new Error(r==="zh"?"ZIP 包中缺少 manifest.json 声明文件":"manifest.json missing in ZIP package");const J=await A.async("string"),a=JSON.parse(J);if(!a.id||!a.name)throw new Error(r==="zh"?"manifest.json 缺少必填字段 id 或 name":"manifest.json missing required id or name field");O(a),G(a.capabilitiesProposed||[]),y(a.contributes||null),E(!!$.file("frontend.js")),$e(jt(a)),k("process"),await Promise.all([L(a),P(a.id)])}catch($){se($.message||"ZIP parse error")}finally{ue(!1)}},z=async()=>{try{const d=await fetch("/api/admin/runtime-policies"),$=await d.json().catch(()=>null);d.ok&&typeof $?.policies?.allowUnsigned?.allowByDefault=="boolean"&&Oe($.policies.allowUnsigned.allowByDefault)}catch{}},B=d=>{const $=ss(d);$&&k($)},P=async d=>{p("unknown");try{const $=await fetch(`/api/plugins/isolation-policy?manifestId=${encodeURIComponent(d)}`);if(!$.ok)return;const A=await $.json();p(A?.success===!0&&A?.trusted===!0?"trusted":"untrusted")}catch{}},L=async d=>{if(pe(null),we(!1),Y(!1),!d?.id)return;if(x){const A=n.find(a=>a.id===x);let J=null;try{J=A?JSON.parse(A.manifest):null}catch{J=null}if(J?.id&&J.id!==d.id){se(r==="zh"?`ZIP 的插件 ID(${d.id})与当前卡片(${J.id})不一致,无法更新。`:`ZIP manifest.id (${d.id}) does not match this plugin (${J.id}).`);return}pe({pluginId:x,name:A?.name||J?.name||d.name,version:A?.version||J?.version||"0.0.0",status:A?.status||"unknown",isSystem:!1,manifestId:d.id}),B(A?.isolation_mode);return}const $=n.find(A=>{try{return JSON.parse(A.manifest)?.id===d.id}catch{return!1}});if($){let A={};try{A=JSON.parse($.manifest)}catch{A={}}pe({pluginId:$.id,name:$.name||A.name||d.name,version:$.version||A.version||"0.0.0",status:$.status,isSystem:!1,manifestId:d.id}),B($.isolation_mode);return}try{const J=await(await fetch(`/api/plugins/by-manifest/${encodeURIComponent(d.id)}`)).json();if(J?.success&&J.installed){if(J.isSystem){se(r==="zh"?"系统核心插件禁止通过插件中心更新。":"System plugins cannot be updated here.");return}pe({pluginId:J.pluginId,name:J.name,version:J.version||"0.0.0",status:J.status,isSystem:!!J.isSystem,manifestId:d.id}),B(J.isolationMode)}}catch(A){console.warn("Failed to detect existing plugin install",A)}};o.useEffect(()=>{if(g!==3||!ee)return;let d=!1;return(async()=>{W(!0);try{const $=ee.file("frontend.js"),A=$?await $.async("string"):"",J=jt(b),a=rs(A);if(d)return;$e(as(J,a)),I([{name:"index.js",size:ee.file("index.js")?(await ee.file("index.js").async("string")).length:0},{name:"frontend.js",size:A.length},{name:"manifest.json",size:ee.file("manifest.json")?(await ee.file("manifest.json").async("string")).length:0}])}catch($){console.warn("Extension point preview failed:",$)}finally{d||W(!1)}})(),()=>{d=!0}},[g,ee,b,N]);const q=o.useMemo(()=>{const d=ne==="teacher"?ns:os,$=Ne.filter(A=>d.has(A.slot));return $.length>0?$:Ne},[Ne,ne]),Ue=M==="trusted",Me=Ue?j:"process",V=!!H,le=V&&is(b?.version||"0.0.0",H.version)<0,_e=V&&H.status==="active"&&(Ne.some(d=>["classroom.tool","student.view","teacher.tab","workspace.view"].includes(d.slot))||!!(b?.classroomTools&&b.classroomTools.length>0)||!!(b?.contributes&&Object.keys(b.contributes).length>0)),me=!Le&&Ae===!1,Ve=!(!i||!b||H?.isSystem||le&&!Z||_e&&!ge||me&&!oe),qe={allowUnsigned:me&&oe},et=async()=>{if(!(!i||!Ve)){ae(!0),se(null),xe(10),he(r==="zh"?V?"正在上传并更新...":"正在上传并安装...":V?"Uploading and updating...":"Uploading and installing...");try{await c(i,Me,{mode:V?"update":"install",targetPluginId:H?.pluginId,allowDowngrade:le?Z:!1,approvePublicRoutes:f,approveCapabilities:Q,approveNetworkHosts:Ce,...qe}),xe(100),he(r==="zh"?V?"更新完成":"安装完成":V?"Updated":"Complete"),t()}catch(d){console.error(d),se(d?.message||(r==="zh"?V?"更新失败":"安装失败":V?"Update failed":"Installation failed")),xe(0),he("")}finally{ae(!1)}}},De=async()=>{if(!(!i||Ee||ve)&&!(me&&!oe)){l(!0),we(!0),Y(!0),ae(!0),se(null),xe(20),he(r==="zh"?V?"极速同意全套授权,正在更新...":"极速同意全套授权,正在安装...":V?"Express approving terms & updating...":"Express approving terms & installing...");try{await c(i,Me,{mode:V?"update":"install",targetPluginId:H?.pluginId,allowDowngrade:!0,approvePublicRoutes:!0,approveCapabilities:!0,approveNetworkHosts:!0,...qe}),xe(100),he(r==="zh"?V?"更新完成":"安装完成":V?"Updated":"Complete"),t()}catch(d){console.error(d),se(d?.message||(r==="zh"?V?"更新失败":"安装失败":V?"Update failed":"Installation failed")),xe(0),he("")}finally{ae(!1)}}};if(!s)return null;const He=es(Array.isArray(b?.network?.outbound)?b.network.outbound:[]),Je=He.filter(d=>d.approvable).map(d=>d.host),je=He.filter(d=>!d.approvable).map(d=>({raw:d.raw,reason:d.reason||""})),We=d=>{const $=["db:schema","management:write","network:access"],A=["whiteboard:write","vfs:write","lesson:write","class:write"];return $.includes(d)?{level:"high",label:r==="zh"?"🔴 高危":"🔴 High",desc:r==="zh"?"可读写数据库、获取网络数据,存在敏感信息暴露与跨站点数据隐患。":"Can read/write DB or access network, sensitive data risk.",rowClass:"bg-rose-50/80 border-rose-200/90 hover:border-rose-300 shadow-2xs text-rose-950",badgeClass:"bg-rose-100 text-rose-700 border-rose-200 font-extrabold",codeClass:"bg-white border-rose-200 text-rose-900",descClass:"text-rose-800/90"}:A.includes(d)?{level:"medium",label:r==="zh"?"🟡 中危":"🟡 Medium",desc:r==="zh"?"可在课表、白板中进行绘制修改或写入局部业务持久化数据。":"Can write to whiteboards or control course schedules.",rowClass:"bg-amber-50/80 border-amber-200/90 hover:border-amber-300 shadow-2xs text-amber-950",badgeClass:"bg-amber-100 text-amber-800 border-amber-200 font-extrabold",codeClass:"bg-white border-amber-200 text-amber-900",descClass:"text-amber-800/90"}:{level:"low",label:r==="zh"?"🟢 低危":"🟢 Low",desc:r==="zh"?"常规的基础界面弹窗与局域隔离存储能力,安全可信。":"Basic UI elements or isolated local storage.",rowClass:"bg-emerald-50/70 border-emerald-200/90 hover:border-emerald-300 shadow-2xs text-emerald-950",badgeClass:"bg-emerald-100 text-emerald-800 border-emerald-200 font-extrabold",codeClass:"bg-white border-emerald-200 text-emerald-900",descClass:"text-emerald-800/90"}};return e.jsx("div",{className:"fixed inset-0 bg-slate-900/60 backdrop-blur-2xs flex items-center justify-center overflow-y-auto select-none",style:{zIndex:9999},children:e.jsxs("div",{className:"bg-white border border-slate-200 shadow-2xl rounded-2xl w-full max-w-2xl p-6 md:p-7 relative flex flex-col justify-between max-h-[90vh]",children:[e.jsxs("div",{className:"flex justify-between items-center border-b border-slate-100 pb-4 shrink-0",children:[e.jsxs("h2",{className:"font-extrabold text-base md:text-lg text-slate-800 flex items-center gap-2",children:[e.jsx(ct,{className:"text-indigo-600 animate-pulse",size:20}),r==="zh"?`${V?"三方插件更新向导":"三方插件安装向导"} (${g+1}/5)`:`${V?"Plugin Update Wizard":"Plugin Setup Wizard"} (${g+1}/5)`]}),e.jsx("button",{onClick:t,className:"text-gray-400 hover:text-gray-650 cursor-pointer",children:e.jsx(Se,{size:18})})]}),e.jsx("div",{className:"flex-1 overflow-y-auto py-5 min-h-[300px]",children:Ee?e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-indigo-600 gap-2",children:[e.jsx(K,{size:36,className:"animate-spin"}),e.jsx("span",{className:"text-sm font-semibold",children:r==="zh"?"正在解压并审计文件...":"Extracting and auditing package..."})]}):fe?e.jsxs("div",{className:"bg-red-50 border border-red-200 rounded-xl p-4 text-red-700 flex items-start gap-2.5",children:[e.jsx(Pe,{size:18,className:"shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-bold text-sm",children:r==="zh"?"校验未通过,安装中断":"Verification Failed"}),e.jsx("p",{className:"text-xs mt-1 leading-relaxed",children:fe})]})]}):e.jsxs(e.Fragment,{children:[g===0&&b&&e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"bg-indigo-50/50 border border-indigo-100 rounded-xl p-4 flex gap-4",children:[e.jsx("div",{className:"w-12 h-12 bg-indigo-600 text-white rounded-lg flex items-center justify-center shrink-0 shadow-sm font-bold text-lg uppercase",children:b.name.charAt(0)}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-extrabold text-slate-900 text-base",children:b.name}),e.jsxs("p",{className:"text-xs font-mono text-gray-500 mt-1",children:["ID: ",b.id," | v",b.version||"1.0.0"]})]})]}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-bold text-slate-700 text-xs mb-1.5",children:r==="zh"?"功能描述":"Description"}),e.jsx("p",{className:"text-xs text-gray-600 leading-relaxed bg-slate-50 border border-slate-100 p-3 rounded-lg font-medium",children:b.description||(r==="zh"?"该插件暂无详细描述信息。":"No description provided for this plugin.")})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4 mt-2",children:[e.jsxs("div",{className:"border border-slate-100 rounded-xl p-3 bg-slate-50/30",children:[e.jsx("span",{className:"text-xs text-gray-400 block font-semibold",children:r==="zh"?"主程序入口":"Main Entry"}),e.jsx("span",{className:"text-xs font-bold text-slate-700 font-mono mt-0.5 block",children:b.main||"index.js"})]}),e.jsxs("div",{className:"border border-slate-100 rounded-xl p-3 bg-slate-50/30",children:[e.jsx("span",{className:"text-xs text-gray-400 block font-semibold",children:r==="zh"?"前端沙箱包":"Frontend UI Asset"}),e.jsx("span",{className:`text-xs font-bold mt-0.5 block ${N?"text-indigo-600":"text-gray-500"}`,children:N?r==="zh"?"包含 (frontend.js)":"Included (frontend.js)":r==="zh"?"无":"None"})]})]}),H&&e.jsxs("div",{className:`mt-1 rounded-xl border p-4 ${le?"border-red-200 bg-red-50/50":"border-amber-200 bg-amber-50/40"}`,children:[e.jsx("h4",{className:"text-xs font-extrabold text-slate-800 mb-2",children:r==="zh"?"检测到已安装同一插件 — 将执行原地更新":"Same plugin already installed — in-place update"}),e.jsxs("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[e.jsxs("div",{className:"bg-white/70 border border-slate-100 rounded-lg p-2",children:[e.jsx("div",{className:"text-slate-400 font-semibold",children:r==="zh"?"当前版本":"Installed"}),e.jsxs("div",{className:"font-bold text-slate-800 mt-0.5",children:[H.name,e.jsxs("span",{className:"font-mono text-slate-500 ml-1",children:["v",H.version]})]}),e.jsx("div",{className:"text-xs text-slate-400 mt-1 font-mono",children:H.status==="active"?r==="zh"?"状态: 运行中":"status: active":r==="zh"?"状态: 未启用":"status: inactive"})]}),e.jsxs("div",{className:"bg-white/70 border border-slate-100 rounded-lg p-2",children:[e.jsx("div",{className:"text-slate-400 font-semibold",children:r==="zh"?"ZIP 新版本":"Package"}),e.jsxs("div",{className:"font-bold text-slate-800 mt-0.5",children:[b.name,e.jsxs("span",{className:"font-mono text-indigo-600 ml-1",children:["v",b.version||"0.0.0"]})]}),e.jsx("div",{className:"text-xs text-slate-400 mt-1",children:r==="zh"?"保留配置 / 业务数据 / UUID":"Keeps config, data & UUID"})]})]}),le&&e.jsxs("label",{className:"mt-3 flex items-start gap-2 text-xs font-bold text-red-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:Z,onChange:d=>we(d.target.checked)}),e.jsx("span",{children:r==="zh"?`确认强制降级:v${H.version} → v${b.version||"0.0.0"}(可能不兼容)`:`Force downgrade v${H.version} → v${b.version||"0.0.0"} (may be incompatible)`})]}),_e&&e.jsxs("label",{className:"mt-2 flex items-start gap-2 text-xs font-bold text-amber-800 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:ge,onChange:d=>Y(d.target.checked)}),e.jsx("span",{children:r==="zh"?"该插件当前为启用状态且声明了课堂/教学扩展点,热更新可能导致课中 UI 短暂异常。我已知晓并继续。":"Plugin is active with classroom UI contributions. Hot-update may briefly disrupt live class UI. I understand."})]})]}),me&&e.jsxs("div",{className:"rounded-xl border border-red-200 bg-red-50/70 p-3","data-testid":"unsigned-plugin-ack",children:[e.jsx("p",{className:"text-xs font-extrabold text-red-800",children:r==="zh"?"未签名本地插件:当前站点策略已收紧":"Unsigned local plugin: site policy is tightened"}),e.jsx("p",{className:"text-xs text-red-700 mt-1 leading-relaxed",children:r==="zh"?"此 ZIP 不含 signature.json。允许未签名不等于全信任,也不会绕过签名错误、公钥、网络限制、权限或沙箱。请逐次确认后再安装。一键极速安装不能跳过此项。":"This ZIP has no signature.json. Allowing unsigned packages is not full trust and cannot bypass signature errors, public keys, network limits, permissions, or the sandbox. Confirm each upload. Express install cannot skip this."}),e.jsxs("label",{className:"mt-2 flex items-start gap-2 text-xs font-bold text-red-800 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:oe,onChange:d=>ie(d.target.checked)}),e.jsx("span",{children:r==="zh"?"我已了解未签名包的风险,并确认本次上传该未签名本地插件。":"I understand the unsigned-package risk and confirm this unsigned local upload."})]})]})]}),g===1&&e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("p",{className:"text-xs text-slate-500 font-medium mb-1",children:r==="zh"?"此插件申请了以下系统权限,请仔细评估安装后可能产生的风控隐患:":"This plugin requests the following capabilities, please review carefully:"}),D.length===0?e.jsx("div",{className:"text-center p-8 bg-emerald-50 border border-emerald-100 rounded-xl text-emerald-700 font-bold text-xs",children:r==="zh"?"✓ 该插件未申请任何敏感系统权限,可安全安装。":"✓ This plugin requires no system capabilities, very safe."}):e.jsx("div",{className:"space-y-3 max-h-[220px] overflow-y-auto pr-1",children:D.map(d=>{const $=We(d);return e.jsxs("div",{className:`border rounded-xl p-3.5 flex flex-col gap-1 transition-all ${$.rowClass}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:`font-mono text-xs font-bold rounded px-2 py-0.5 shadow-2xs ${$.codeClass}`,children:d}),e.jsx("span",{className:`text-xs px-2 py-0.5 rounded-md border ${$.badgeClass}`,children:$.label})]}),e.jsx("p",{className:`text-xs leading-relaxed font-medium mt-1 ${$.descClass}`,children:$.desc})]},d)})}),D.length>0&&e.jsxs("label",{className:"flex items-start gap-2 border border-orange-100 rounded-xl p-3 bg-orange-50/30 cursor-pointer mt-2 text-xs text-orange-850 font-bold leading-normal",children:[e.jsx("input",{type:"checkbox",checked:m,onChange:d=>l(d.target.checked),className:"mt-0.5 w-3.5 h-3.5 rounded text-orange-600 border-orange-300 focus:ring-orange-500 cursor-pointer shrink-0"}),e.jsx("span",{children:r==="zh"?"我已了解并信任此插件的提供商,愿意授予上述高级系统权限。":"I trust the developer and agree to grant all requested capabilities."})]})]}),g===2&&e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("p",{className:"text-xs text-slate-500 font-medium",children:r==="zh"?"插件将向系统注册并扩展以下槽位(Slots)与自定义命令:":"The plugin will inject UI slots or register custom commands:"}),e.jsx("div",{className:"border border-slate-150/65 rounded-xl p-4 bg-slate-50/50 space-y-4",children:R?e.jsxs(e.Fragment,{children:[R.teacher?.dashboard?.widget&&e.jsxs("div",{className:"text-xs",children:[e.jsx("span",{className:"font-bold text-slate-700 block mb-1",children:"插槽: 教师端侧栏卡片"}),e.jsxs("span",{className:"text-gray-500 font-mono",children:["teacher.dashboard.widget → ",R.teacher.dashboard.widget]})]}),R.student?.view&&e.jsxs("div",{className:"text-xs",children:[e.jsx("span",{className:"font-bold text-slate-700 block mb-1",children:"插槽: 学生答题看板"}),e.jsxs("span",{className:"text-gray-500 font-mono",children:["student.view → ",R.student.view]})]}),b.capabilitiesProposed&&b.capabilitiesProposed.length>0&&e.jsxs("div",{className:"text-xs border-t border-slate-100 pt-3",children:[e.jsx("span",{className:"font-bold text-slate-700 block mb-1",children:r==="zh"?"注册指令集":"Commands Registered"}),e.jsx("div",{className:"flex gap-1.5 flex-wrap",children:b.capabilitiesProposed.map(d=>e.jsx("span",{className:"bg-indigo-50 border border-indigo-150/60 rounded px-1.5 py-0.5 text-indigo-700 font-mono",children:d.replace(":write",".create")},d))})]})]}):e.jsx("div",{className:"text-center text-xs text-slate-400 italic py-6",children:r==="zh"?"此插件没有提供任何额外的槽位注册项。":"This plugin contributes no extension points."})})]}),g===3&&e.jsxs("div",{className:"flex flex-col gap-3 h-full",children:[e.jsxs("div",{className:"flex items-center justify-between shrink-0 gap-3",children:[e.jsx("p",{className:"text-xs text-slate-500 font-semibold",children:r==="zh"?"检测到的扩展点(静态分析预览):":"Detected extension points (static analysis):"}),e.jsxs("div",{className:"flex bg-slate-100 p-0.5 rounded-lg border border-slate-200 shrink-0",children:[e.jsx("button",{type:"button",onClick:()=>Ie("teacher"),className:`px-3 py-1 rounded-md text-xs font-bold transition-all cursor-pointer ${ne==="teacher"?"bg-white shadow text-indigo-650":"text-slate-550 hover:text-slate-800"}`,children:r==="zh"?"教师端相关":"Teacher slots"}),e.jsx("button",{type:"button",onClick:()=>Ie("student"),className:`px-3 py-1 rounded-md text-xs font-bold transition-all cursor-pointer ${ne==="student"?"bg-white shadow text-indigo-650":"text-slate-550 hover:text-slate-800"}`,children:r==="zh"?"学生端相关":"Student slots"})]})]}),e.jsx("div",{className:"border border-slate-200/80 rounded-xl p-4 bg-slate-50/50 flex-grow overflow-auto min-h-[220px] max-h-[300px]",children:e.jsx("div",{className:"h-full bg-white rounded-lg p-4 border border-slate-100 shadow-sm",children:Re?e.jsxs("div",{className:"flex flex-col items-center justify-center h-full gap-2 text-indigo-600",children:[e.jsx(K,{size:22,className:"animate-spin"}),e.jsx("span",{className:"text-xs font-semibold",children:r==="zh"?"正在分析扩展点…":"Analyzing extension points…"})]}):q.length===0?e.jsx("div",{className:"flex items-center justify-center h-full text-center text-xs text-slate-400 italic px-4",children:r==="zh"?"未检测到扩展点注册(manifest.contributes / frontend.js)。":"No extension points detected in manifest or frontend.js."}):e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx("div",{className:"text-xs font-semibold text-slate-500",children:r==="zh"?`共 ${q.length} 项(按「${ne==="teacher"?"教师端":"学生端"}」筛选)`:`${q.length} item(s) for ${ne} view`}),e.jsx("ul",{className:"space-y-2",children:q.map(d=>e.jsxs("li",{className:"flex items-start justify-between gap-3 rounded-lg border border-slate-100 bg-slate-50/60 px-3 py-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm font-bold text-slate-800 truncate",children:d.label}),e.jsxs("div",{className:"text-xs text-slate-500 font-mono mt-0.5 truncate",children:[d.slot,d.id&&d.id!=="?"?` · ${d.id}`:""]})]}),e.jsx("span",{className:`shrink-0 text-xs font-semibold px-1.5 py-0.5 rounded border ${d.source==="manifest"?"bg-emerald-50 text-emerald-700 border-emerald-100":"bg-indigo-50 text-indigo-700 border-indigo-100"}`,children:d.source==="manifest"?"manifest":"frontend.js"})]},`${d.source}-${d.slot}-${d.id}`))}),v.length>0&&e.jsxs("div",{className:"pt-2 border-t border-slate-100",children:[e.jsx("div",{className:"text-xs font-semibold text-slate-400 mb-1",children:r==="zh"?"包内文件":"Package files"}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:v.map(d=>e.jsxs("span",{className:"text-xs font-mono bg-white border border-slate-150 rounded px-1.5 py-0.5 text-slate-600",children:[d.name,d.size>0?` (${Math.max(1,Math.round(d.size/1024))} KB)`:""]},d.name))})]})]})})}),e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-gray-550 font-medium shrink-0 bg-indigo-50/40 p-2 border border-indigo-100/40 rounded-lg",children:[e.jsx(xt,{size:11,className:"text-indigo-600"}),e.jsx("span",{children:r==="zh"?"此步骤为静态扫描结果,不会执行插件代码。中文标签已从 JS 转义序列还原;优先展示 manifest 声明,frontend.js 作为补充。":"Static scan only — plugin code is not executed. Unicode escapes in JS labels are decoded; manifest entries take priority over frontend.js."})]})]}),g===4&&e.jsxs("div",{className:"flex flex-col gap-4",children:[me&&e.jsxs("div",{className:"rounded-xl border border-red-200 bg-red-50/70 p-3","data-testid":"unsigned-plugin-ack",children:[e.jsx("p",{className:"text-xs font-extrabold text-red-800",children:r==="zh"?"未签名本地插件:当前站点策略已收紧":"Unsigned local plugin: site policy is tightened"}),e.jsx("p",{className:"text-xs text-red-700 mt-1 leading-relaxed",children:r==="zh"?"此 ZIP 不含 signature.json。允许未签名不等于全信任,也不会绕过签名错误、公钥、网络限制、权限或沙箱。请逐次确认后再安装。一键极速安装不能跳过此项。":"This ZIP has no signature.json. Allowing unsigned packages is not full trust and cannot bypass signature errors, public keys, network limits, permissions, or the sandbox. Confirm each upload. Express install cannot skip this."}),e.jsxs("label",{className:"mt-2 flex items-start gap-2 text-xs font-bold text-red-800 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:oe,onChange:d=>ie(d.target.checked)}),e.jsx("span",{children:r==="zh"?"我已了解未签名包的风险,并确认本次上传该未签名本地插件。":"I understand the unsigned-package risk and confirm this unsigned local upload."})]})]}),H&&e.jsxs("div",{className:`rounded-xl border p-3 text-xs ${le?"border-red-200 bg-red-50/60":"border-amber-200 bg-amber-50/50"}`,children:[e.jsx("div",{className:"font-extrabold text-slate-800 mb-1",children:r==="zh"?`更新确认:v${H.version} → v${b?.version||"0.0.0"}`:`Update: v${H.version} → v${b?.version||"0.0.0"}`}),e.jsx("div",{className:"text-slate-600",children:r==="zh"?"将原地替换代码与静态资源,保留 UUID、配置与业务数据。":"Replaces code/assets in place. UUID, config and data are preserved."}),le&&e.jsxs("label",{className:"mt-2 flex items-start gap-2 font-bold text-red-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:Z,onChange:d=>we(d.target.checked)}),e.jsx("span",{children:r==="zh"?"确认强制降级":"Confirm forced downgrade"})]}),_e&&e.jsxs("label",{className:"mt-2 flex items-start gap-2 font-bold text-amber-800 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:ge,onChange:d=>Y(d.target.checked)}),e.jsx("span",{children:r==="zh"?"已知晓课中热更新风险":"I accept live-class hot-update risk"})]}),!Ve&&e.jsx("p",{className:"mt-2 text-red-600 font-semibold",children:r==="zh"?"请先完成上方确认项后再更新。":"Complete the confirmation checks above before updating."})]}),e.jsx("p",{className:"text-xs text-slate-500 font-medium",children:M==="trusted"?r==="zh"?"该插件的 id 已在服务端受信名单(OPENLEARN_TRUSTED_INLINE_PLUGINS 或内置目录),可选执行隔离档位:":"This id is on the server-side trusted list (OPENLEARN_TRUSTED_INLINE_PLUGINS or the built-in directory); you may choose the isolation tier:":r==="zh"?"设置该插件启动时的执行隔离:":"Choose how this plugin is isolated at startup:"}),Ue?e.jsxs("div",{className:"space-y-3",children:[e.jsxs("label",{className:`border rounded-xl p-4 flex gap-3 cursor-pointer transition-all ${j==="process"?"bg-indigo-50/40 border-indigo-300 shadow-xs":"border-slate-150 hover:border-indigo-200 bg-white"}`,children:[e.jsx("input",{type:"radio",name:"sandbox_mode",checked:j==="process",onChange:()=>k("process"),className:"mt-1 w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer shrink-0"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-extrabold text-xs md:text-sm text-slate-800",children:r==="zh"?"子进程隔离":"Child-process isolation"}),e.jsx("p",{className:"text-xs text-slate-550 mt-1 leading-normal font-medium",children:r==="zh"?"独立进程,最小环境变量。崩溃与堆上限止于该进程。":"Separate process with a minimal environment. Crashes and the heap cap stay in that process."})]})]}),e.jsxs("label",{className:`border rounded-xl p-4 flex gap-3 cursor-pointer transition-all ${j==="worker"?"bg-indigo-50/40 border-indigo-300 shadow-xs":"border-slate-150 hover:border-indigo-200 bg-white"}`,children:[e.jsx("input",{type:"radio",name:"sandbox_mode",checked:j==="worker",onChange:()=>k("worker"),className:"mt-1 w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer shrink-0"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-extrabold text-xs md:text-sm text-slate-800",children:r==="zh"?"Worker 线程(仅受信内置)":"Worker thread (trusted built-ins only)"}),e.jsx("p",{className:"text-xs text-slate-550 mt-1 leading-normal font-medium",children:r==="zh"?"同进程的 worker_threads,会看到宿主环境变量。只适合明确受信任的内置插件。":"Same-process worker_threads. It can see host environment variables. Trusted built-ins only."})]})]}),e.jsxs("label",{className:`border rounded-xl p-4 flex gap-3 cursor-pointer transition-all ${j==="inline"?"bg-indigo-50/40 border-indigo-300 shadow-xs":"border-slate-150 hover:border-indigo-200 bg-white"}`,children:[e.jsx("input",{type:"radio",name:"sandbox_mode",checked:j==="inline",onChange:()=>k("inline"),className:"mt-1 w-4 h-4 text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer shrink-0"}),e.jsxs("div",{children:[e.jsx("h4",{className:"font-extrabold text-xs md:text-sm text-slate-800",children:r==="zh"?"主进程(仅受信内置)":"In-process (trusted built-ins only)"}),e.jsx("p",{className:"text-xs text-slate-550 mt-1 leading-normal font-medium",children:r==="zh"?"直接跑在宿主进程里。未捕获错误可能拖垮服务。":"Runs inside the host process. An uncaught error can take the server down."})]})]})]}):e.jsxs("div",{"data-testid":"wizard-isolation-lock",className:"border border-sky-200 bg-sky-50/70 rounded-xl p-4 text-xs text-sky-950 leading-relaxed",children:[e.jsx("h4",{className:"font-extrabold text-sm",children:r==="zh"?"子进程隔离(已锁定)":"Child-process isolation (locked)"}),e.jsx("p",{className:"mt-1",children:r==="zh"?"上传的包固定以独立子进程运行,不继承密钥类环境变量。id 前缀不能证明它是内置插件。只有内置目录或 OPENLEARN_TRUSTED_INLINE_PLUGINS 白名单里的包才可以进程内运行,而且那一步由服务端决定。":"Uploaded packages stay in a child process and do not inherit secret environment variables. An id prefix does not make a package built-in. Only the built-in directory or OPENLEARN_TRUSTED_INLINE_PLUGINS can run in-process, and the server makes that decision."}),e.jsx("p",{className:"mt-2 text-sky-900/80",children:r==="zh"?"需要主进程或 Worker 运行?把该包的 manifest.id 加进 OPENLEARN_TRUSTED_INLINE_PLUGINS 并重启服务,然后重新选择档位。":"Need in-process or worker execution? Add this package’s manifest.id to OPENLEARN_TRUSTED_INLINE_PLUGINS, restart the server, then re-select the tier."})]}),e.jsxs("label",{className:"flex items-start gap-2 text-xs text-slate-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:f,onChange:d=>F(d.target.checked)}),e.jsx("span",{children:r==="zh"?"批准清单中声明为公开的路由(auth: false)。不勾选则这些路由仍要求登录。":"Approve routes the manifest marks public (auth: false). Leave unchecked and those routes still require a session."})]}),e.jsxs("label",{className:"flex items-start gap-2 text-xs text-slate-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:Q,onChange:d=>de(d.target.checked)}),e.jsx("span",{children:r==="zh"?"确认授予清单申请的能力。不勾选则这些能力不会生效。通配符 * 不会被授予。":"Approve the capabilities this manifest requests. Leave unchecked and they stay inactive. Wildcard capabilities are never granted."})]}),Je.length>0&&e.jsxs("div",{className:"border border-amber-200 bg-amber-50/60 rounded-lg p-3 space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{className:"text-xs font-extrabold text-amber-900",children:r==="zh"?"出站网络域名(清单申请)":"Outbound hosts requested by the manifest"}),e.jsx("span",{className:"text-[10px] font-mono text-amber-700 bg-amber-100 border border-amber-200 rounded px-1.5 py-0.5",children:Je.length})]}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:Je.map(d=>{const $=je.some(A=>A.raw===d);return e.jsx("span",{title:$?je.find(A=>A.raw===d)?.reason:r==="zh"?"批准后该域名可被插件访问":"Approve to allow the plugin to reach this host",className:"font-mono text-[11px] rounded px-1.5 py-0.5 border "+($?"bg-rose-50 text-rose-700 border-rose-200 line-through":"bg-white text-slate-700 border-slate-200"),children:d},d)})}),je.length>0&&e.jsx("p",{className:"text-[11px] text-rose-700 leading-relaxed",children:r==="zh"?`${je.length} 项声明永远不会被批准:${je.map(d=>`${d.raw}(${d.reason})`).join("、")}`:`${je.length} declaration(s) can never be approved: ${je.map(d=>`${d.raw} (${d.reason})`).join(", ")}`}),e.jsxs("label",{className:"flex items-start gap-2 text-xs text-slate-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5","data-testid":"approve-network-hosts",checked:Ce,onChange:d=>U(d.target.checked)}),e.jsx("span",{children:r==="zh"?"批准清单申请的上方可访问域名。不勾选则插件无法发起任何出站请求(默认拒绝)。私有/回环/通配符即使勾选也不会被授予。":"Approve the outbound hosts above. Leave unchecked and the plugin cannot reach any external host (default deny). Private/loopback/wildcard entries are never granted."})]})]})]})]})}),e.jsxs("div",{className:"flex items-center justify-between border-t border-slate-100 pt-4 shrink-0",children:[e.jsx("div",{className:"flex items-center gap-1.5",children:[0,1,2,3,4].map(d=>e.jsx("div",{className:`h-1.5 rounded-full transition-all duration-300 ${d===g?"w-4 bg-indigo-650":"w-1.5 bg-slate-200"}`},d))}),e.jsxs("div",{className:"flex items-center gap-2",children:[g>0?e.jsxs("button",{type:"button",onClick:()=>T(d=>d-1),className:"flex items-center gap-1.5 px-3.5 py-2 border border-slate-200 rounded-lg text-xs font-semibold text-slate-600 hover:bg-slate-50 transition-colors cursor-pointer",children:[e.jsx(Tt,{size:12}),r==="zh"?"上一步":"Back"]}):e.jsx("button",{type:"button",onClick:t,className:"px-3.5 py-2 border border-slate-200 rounded-lg text-xs font-semibold text-slate-400 hover:bg-slate-50 transition-colors cursor-pointer",children:r==="zh"?"取消":"Cancel"}),!ve&&i&&b&&!H?.isSystem&&e.jsxs("button",{type:"button",onClick:De,disabled:me&&!oe,className:"flex items-center gap-1.5 px-3.5 py-2 bg-gradient-to-r from-amber-500 to-amber-600 hover:from-amber-600 hover:to-amber-700 text-white rounded-lg text-xs font-bold shadow-xs transition-all cursor-pointer disabled:from-gray-300 disabled:to-gray-300 disabled:cursor-not-allowed",title:me&&!oe?r==="zh"?"请先确认未签名风险,一键极速安装不能跳过此项":"Confirm the unsigned-package risk first; express install cannot skip it":r==="zh"?"同意全套权限与提示,直接安装完成":"Approve terms and install directly",children:[e.jsx(Dt,{size:13,className:"fill-amber-200"}),e.jsx("span",{children:r==="zh"?V?"一键极速更新":"一键极速安装":V?"Express Update":"Express Install"})]}),g===1&&D.length>0&&!m?e.jsxs("button",{type:"button",disabled:!0,className:"flex items-center gap-1.5 px-4 py-2 bg-gray-200 text-gray-400 rounded-lg text-xs font-semibold cursor-not-allowed",children:[r==="zh"?"请先授权同意":"Accept terms first",e.jsx(ut,{size:12})]}):g<4?e.jsxs("button",{type:"button",onClick:()=>T(d=>d+1),className:"flex items-center gap-1.5 px-4 py-2 bg-slate-900 text-white rounded-lg text-xs font-semibold hover:bg-slate-800 transition-colors cursor-pointer",children:[r==="zh"?"下一步":"Next",e.jsx(ut,{size:12})]}):e.jsx("button",{type:"button",onClick:et,disabled:ve||!Ve,className:"flex items-center gap-1.5 px-5 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold shadow-sm transition-colors cursor-pointer disabled:bg-indigo-400",children:ve?e.jsxs("div",{className:"flex flex-col gap-1.5 min-w-32",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(K,{size:13,className:"animate-spin"}),e.jsx("span",{children:r==="zh"?V?"更新中...":"安装中...":V?"Updating...":"Installing..."})]}),e.jsx("div",{className:"w-full h-1 bg-indigo-400/30 rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full bg-white/80 rounded-full transition-all duration-500",style:{width:`${Math.max(ye,5)}%`}})}),te&&e.jsxs("div",{className:"text-xs text-indigo-200 truncate max-w-40",children:[ye,"% — ",te]})]}):e.jsxs(e.Fragment,{children:[e.jsx(Lt,{size:13}),r==="zh"?V?"确认更新并上传":"确认安装并上传":V?"Confirm & Update":"Confirm & Install"]})})]})]})]})})}function ds({lang:s}){return e.jsxs("span",{className:"inline-flex items-center gap-1 bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold px-2 py-1 rounded uppercase tracking-wider",title:s==="zh"?"该插件使用旧执行格式运行。新格式版本可能可用。":"This plugin uses the old execution format. A new-format version may be available.",children:[e.jsx(Pe,{size:10}),s==="zh"?"可迁移":"Migratable"]})}function cs({plugins:s,lang:t,isAdmin:r=!0,showSystemPlugins:i,marketMap:c,checkingUpdateId:x,oneClickUpdatingId:n,handleCheckUpdate:g,handleOneClickUpdate:T,setChangelogModalPlugin:b,setSettingsPlugin:O,onOpenSnapshots:D,setUpdateTargetPluginId:G,updateFileInputRef:v,onToggle:I,onDelete:R}){const y=bt(m=>m.dashboardVisibility),[N,E]=be.useState(null);return be.useEffect(()=>{const m=l=>{l.target?.closest(".plugin-action-menu")||E(null)};return document.addEventListener("mousedown",m),()=>document.removeEventListener("mousedown",m)},[]),e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-gray-50/30",children:e.jsx("div",{className:"grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-4 gap-4",children:s.filter(m=>{const l=m.id.startsWith("@openlearn/");return i?!0:!l}).map(m=>{let l={description:"扩展 Openlearn Next 功能的自定义插件。",author:"Community",version:"",manifestId:"",capabilities:[],toolCount:0,studentViewCount:0,teacherWidgetCount:0,hasConfig:!1,repository:"",homepage:""};try{const U=JSON.parse(m.manifest);U.description&&(l.description=U.description),U.author&&(l.author=U.author),U.version&&(l.version=U.version),U.id&&(l.manifestId=U.id),U.repository&&(l.repository=typeof U.repository=="string"?U.repository:U.repository.url||""),U.homepage&&(l.homepage=U.homepage),U.capabilitiesProposed&&(l.capabilities=U.capabilitiesProposed),U.contributes?.["classroom.tool"]?l.toolCount=U.contributes["classroom.tool"].length:U.classroomTools&&(l.toolCount=U.classroomTools.length),U.contributes?.["student.view"]&&(l.studentViewCount=U.contributes["student.view"].length),U.contributes?.["teacher.dashboard.widget"]&&(l.teacherWidgetCount=U.contributes["teacher.dashboard.widget"].length);const ee=U.configuration?.properties;ee&&Object.keys(ee).length>0&&(l.hasConfig=!0),U.updateSource?.type&&U.updateSource?.repo&&(l.updateSource=U.updateSource)}catch{}const j=m.created_at?new Date(m.created_at).toLocaleDateString(t==="zh"?"zh-CN":"en-US",{year:"numeric",month:"short",day:"numeric"}):"—",k=m.created_at?(()=>{const U=new Date(m.created_at);if(isNaN(U.getTime()))return"—";const ee=U.getFullYear(),ce=String(U.getMonth()+1).padStart(2,"0"),fe=String(U.getDate()).padStart(2,"0");return`${ee}-${ce}-${fe}`})():"—",M=m.id.startsWith("@openlearn/"),p=y.get(m.id)??!0,f=c.get(l.manifestId||m.id),F=!!f?.hasUpdate,Q=f?.error||null,de=N===m.id,Ce=!!l.hasConfig||!!l.updateSource||F||!M||m.execution_mode==="legacy";return e.jsxs("div",{className:`bg-white rounded-xl border border-gray-200 p-4 shadow-sm hover:shadow-md transition-all relative group flex flex-col gap-3 h-full ${m.status!=="active"?"opacity-75":""}`,children:[e.jsxs("div",{className:"flex items-center justify-between gap-1.5 w-full text-xs min-h-[22px]",children:[e.jsxs("div",{className:"flex items-center gap-1 flex-wrap min-w-0",children:[M&&e.jsx("span",{className:"text-[10px] font-bold bg-violet-50 text-violet-700 border border-violet-200 px-1.5 py-0.5 rounded uppercase tracking-wider shrink-0",children:t==="zh"?"系统":"SYSTEM"}),m.execution_mode==="esm"&&e.jsx("span",{className:"text-[10px] font-bold bg-indigo-50 text-indigo-700 border border-indigo-150 px-1.5 py-0.5 rounded uppercase tracking-wider shrink-0",children:"ESM"}),m.execution_mode==="legacy"&&e.jsx(ds,{lang:t}),m.isolation_mode==="process"&&e.jsx("span",{className:"text-[10px] font-bold bg-sky-50 text-sky-800 border border-sky-200 px-1.5 py-0.5 rounded shrink-0",children:t==="zh"?"子进程":"PROCESS"}),m.isolation_mode==="worker"&&e.jsx("span",{className:"text-[10px] font-bold bg-indigo-50 text-indigo-700 border border-indigo-200 px-1.5 py-0.5 rounded shrink-0",children:"Worker"}),m.isolation_mode==="inline"&&e.jsx("span",{className:"text-[10px] font-bold bg-amber-50 text-amber-800 border border-amber-200 px-1.5 py-0.5 rounded shrink-0",children:t==="zh"?"进程内":"IN-PROCESS"})]}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0 ml-auto",children:[F&&e.jsx("span",{title:t==="zh"?`点击查看新特性${f.isPrerelease?"(预发布版本)":""}并升级至 v${f.latestVersion}`:`Upgradeable to v${f.latestVersion}${f.isPrerelease?" (pre-release)":""}`,className:`text-[10px] font-bold px-2 py-0.5 rounded-full shadow-sm flex items-center gap-1 shrink-0 cursor-pointer ${f.isPrerelease?"bg-gradient-to-r from-amber-500 to-orange-500 text-white animate-pulse":"bg-gradient-to-r from-blue-600 to-indigo-600 text-white animate-pulse"}`,onClick:()=>b({...m,marketItem:f}),children:e.jsxs("span",{children:[f.isPrerelease?"🔶 ":"⚡ ",t==="zh"?`${f.isPrerelease?"预发布 ":"新版本 "}v${f.latestVersion}`:`${f.isPrerelease?"Pre-release ":"New "}v${f.latestVersion}`]})}),Q&&!F&&e.jsx("span",{title:Q,className:"text-[10px] font-semibold bg-amber-50 text-amber-700 border border-amber-200 px-2 py-0.5 rounded-full flex items-center gap-1 shrink-0 cursor-default",children:e.jsxs("span",{children:["⚠️ ",t==="zh"?"检查失败":"Check failed"]})}),e.jsxs("span",{className:`text-xs font-semibold px-2.5 py-0.5 rounded-full flex items-center gap-1 border transition-all shrink-0 ${m.status==="active"?"bg-emerald-50 text-emerald-700 border-emerald-200":"bg-slate-100 text-slate-500 border-slate-200"}`,children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full ${m.status==="active"?"bg-emerald-500 animate-pulse":"bg-slate-400"}`}),e.jsx("span",{children:m.status==="active"?t==="zh"?"已启用":"ACTIVE":t==="zh"?"已停用":"INACTIVE"})]})]})]}),e.jsxs("div",{className:"flex items-start gap-3 w-full",children:[e.jsx("div",{className:"w-11 h-11 bg-indigo-50 text-indigo-600 rounded-lg flex items-center justify-center border border-indigo-100 group-hover:bg-indigo-600 group-hover:text-white transition-colors shrink-0",children:e.jsx(At,{size:22})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("h4",{className:"font-semibold text-gray-900 line-clamp-1",title:m.name,children:m.name}),l.version&&e.jsxs("span",{className:"text-xs font-mono font-bold bg-gray-100 text-gray-500 border border-gray-200 px-1.5 py-0.5 rounded shrink-0",children:["v",l.version]})]}),l.manifestId&&e.jsx("p",{className:"text-xs text-gray-400 font-mono truncate mt-0.5",title:l.manifestId,children:l.manifestId})]})]}),e.jsx("p",{className:"text-xs text-gray-500 line-clamp-2 leading-relaxed",children:l.description}),(l.toolCount>0||l.studentViewCount>0||l.teacherWidgetCount>0)&&e.jsxs("div",{className:"flex flex-wrap gap-1.5",children:[l.toolCount>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-indigo-50 text-indigo-600 border border-indigo-100 px-2 py-0.5 rounded-full",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-indigo-400"}),t==="zh"?`${l.toolCount} 个课堂工具`:`${l.toolCount} classroom tool${l.toolCount>1?"s":""}`]}),l.studentViewCount>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-sky-50 text-sky-600 border border-sky-100 px-2 py-0.5 rounded-full",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-sky-400"}),t==="zh"?`${l.studentViewCount} 个学生视图`:`${l.studentViewCount} student view${l.studentViewCount>1?"s":""}`]}),l.teacherWidgetCount>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-amber-50 text-amber-600 border border-amber-100 px-2 py-0.5 rounded-full",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-amber-400"}),t==="zh"?`${l.teacherWidgetCount} 个教师组件`:`${l.teacherWidgetCount} teacher widget${l.teacherWidgetCount>1?"s":""}`]}),l.capabilities.length>0&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-rose-50 text-rose-600 border border-rose-100 px-2 py-0.5 rounded-full",children:[e.jsx(ct,{size:9}),t==="zh"?`${l.capabilities.length} 项权限`:`${l.capabilities.length} permission${l.capabilities.length>1?"s":""}`]})]}),e.jsxs("div",{className:"mt-auto flex flex-col gap-2.5 pt-1",children:[e.jsxs("div",{className:"flex items-center justify-between text-xs text-gray-400 border-t border-gray-100 pt-2 gap-2 whitespace-nowrap overflow-hidden",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0 flex-1 truncate",children:[e.jsxs("span",{className:"flex items-center gap-1 min-w-0 truncate",title:l.author,children:[e.jsx(Ot,{size:11,className:"shrink-0 text-gray-400"}),e.jsx("span",{className:"truncate",children:l.author})]}),ze(l.repository||l.homepage||f?.repository)&&e.jsxs("a",{href:ze(l.repository||l.homepage||f?.repository),target:"_blank",rel:"noopener noreferrer",title:t==="zh"?"查看 Git 开源仓库 (GitHub/Gitee)":"View Git Repository",className:"inline-flex items-center gap-1 text-[11px] font-mono text-indigo-600 hover:text-indigo-800 bg-indigo-50 hover:bg-indigo-100 px-1.5 py-0.5 rounded border border-indigo-200/60 transition-colors shrink-0",children:[e.jsx(mt,{size:10}),e.jsx("span",{className:"truncate max-w-[90px]",children:(l.repository||l.homepage||f?.repository).replace(/^https?:\/\//,"")})]})]}),e.jsxs("span",{className:"flex items-center gap-1 shrink-0 ml-auto font-mono text-[11px] text-gray-400",title:t==="zh"?`安装于: ${j}`:`Installed: ${j}`,children:[e.jsx(Mt,{size:11,className:"shrink-0 text-gray-400"}),e.jsx("span",{children:k})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5 relative min-h-[30px]",children:[e.jsx("button",{onClick:()=>I(m.id),className:`px-2.5 py-1 text-xs font-medium rounded-lg transition-colors cursor-pointer shrink-0 ${m.status==="active"?"bg-amber-50 text-amber-600 hover:bg-amber-100":"bg-green-50 text-green-600 hover:bg-green-100"}`,children:m.status==="active"?t==="zh"?"禁用":"Disable":t==="zh"?"启用":"Enable"}),e.jsxs("button",{onClick:()=>{const U=!p;bt.getState().setDashboardVisibility(m.id,U)},title:p?t==="zh"?"在系统总览中隐藏":"Hide from Dashboard":t==="zh"?"在系统总览中显示":"Show in Dashboard",className:`px-2 py-1 text-xs font-medium rounded-lg transition-colors flex items-center gap-1.5 shrink-0 cursor-pointer ${p?"bg-indigo-50 text-indigo-700 hover:bg-indigo-100 border border-indigo-200/60":"bg-gray-50 text-gray-500 hover:bg-gray-100 border border-gray-200/60"}`,children:[e.jsx("span",{children:t==="zh"?"总览":"Dash"}),e.jsx("span",{className:`w-6 h-3.5 rounded-full transition-colors flex items-center p-0.5 shrink-0 ${p?"bg-indigo-600":"bg-gray-300"}`,children:e.jsx("span",{className:`w-2.5 h-2.5 rounded-full bg-white shadow-sm transition-transform ${p?"translate-x-2.5":"translate-x-0"}`})})]}),F&&e.jsx("button",{onClick:()=>{f?.downloadUrl?T(m.id,f):b({...m,marketItem:f})},disabled:n===m.id,className:"px-2.5 py-1 text-xs font-semibold rounded-lg bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700 shadow-sm transition-all flex items-center gap-1 shrink-0 disabled:opacity-50 cursor-pointer",title:f?.downloadUrl?t==="zh"?`热更新至 v${f.latestVersion}`:`Update to v${f.latestVersion}`:t==="zh"?`查看 v${f.latestVersion} 更新详情`:`View v${f.latestVersion} update details`,children:n===m.id?e.jsx(Fe,{size:11,className:"animate-spin"}):e.jsx("span",{children:f?.downloadUrl?`⚡ ${t==="zh"?"更新":"Update"}`:`⚡ ${t==="zh"?"新版本":"Update"}`})}),Ce&&e.jsxs("div",{className:"relative plugin-action-menu shrink-0 ml-auto",children:[e.jsx("button",{type:"button",onClick:U=>{U.stopPropagation(),E(de?null:m.id)},className:`p-1.5 rounded-lg border transition-colors cursor-pointer flex items-center justify-center ${de?"bg-gray-200 text-gray-800 border-gray-300":"bg-gray-50 hover:bg-gray-100 text-gray-600 border-gray-200"}`,title:t==="zh"?"更多操作":"More actions","aria-label":t==="zh"?"更多操作":"More actions",children:e.jsx(_t,{size:14})}),de&&e.jsxs("div",{className:"absolute right-0 bottom-full mb-1.5 w-40 bg-white border border-gray-200 rounded-xl shadow-xl py-1 z-30 text-xs animate-in fade-in zoom-in-95 duration-100",children:[l.hasConfig&&e.jsxs("button",{onClick:()=>{E(null),O({id:m.id,name:m.name,manifest:m.manifest})},className:"w-full text-left px-3 py-1.5 text-gray-700 hover:bg-gray-50 flex items-center gap-2 transition-colors cursor-pointer",children:[e.jsx(Ze,{size:13,className:"text-gray-500"}),e.jsx("span",{children:t==="zh"?"插件设置":"Settings"})]}),l.updateSource&&e.jsxs("button",{onClick:()=>{E(null),g(m.id,l.manifestId||m.id)},disabled:x===m.id,className:"w-full text-left px-3 py-1.5 text-gray-700 hover:bg-gray-50 flex items-center gap-2 transition-colors cursor-pointer disabled:opacity-50",children:[e.jsx(Fe,{size:13,className:`text-gray-500 ${x===m.id?"animate-spin":""}`}),e.jsx("span",{children:t==="zh"?"检查更新":"Check for updates"})]}),F&&e.jsxs("button",{onClick:()=>{E(null),b({...m,marketItem:f})},className:"w-full text-left px-3 py-1.5 text-gray-700 hover:bg-gray-50 flex items-center gap-2 transition-colors cursor-pointer",children:[e.jsx(Ft,{size:13,className:"text-indigo-500"}),e.jsx("span",{children:t==="zh"?"版本新特性":"Release Notes"})]}),!M&&r&&D&&e.jsxs("button",{onClick:()=>{E(null),D({id:m.id,name:m.name,version:l.version,manifestId:l.manifestId||m.id})},className:"w-full text-left px-3 py-1.5 text-gray-700 hover:bg-gray-50 flex items-center gap-2 transition-colors cursor-pointer",children:[e.jsx(zt,{size:13,className:"text-amber-600"}),e.jsx("span",{children:t==="zh"?"历史快照与回滚":"Snapshots & Rollback"})]}),!M&&e.jsxs("button",{onClick:()=>{E(null),G(m.id),v.current?.click()},className:"w-full text-left px-3 py-1.5 text-gray-700 hover:bg-gray-50 flex items-center gap-2 transition-colors cursor-pointer",children:[e.jsx(Ke,{size:13,className:"text-gray-500"}),e.jsx("span",{children:t==="zh"?"上传更新包":"Upload Update"})]}),m.execution_mode==="legacy"&&e.jsxs("button",{onClick:()=>{E(null),document.getElementById("zip-plugin-uploader")?.click()},className:"w-full text-left px-3 py-1.5 text-amber-700 hover:bg-amber-50 flex items-center gap-2 transition-colors cursor-pointer",children:[e.jsx(Fe,{size:13,className:"text-amber-600"}),e.jsx("span",{children:t==="zh"?"迁移格式":"Migrate"})]}),!M&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"border-t border-gray-100 my-1"}),e.jsxs("button",{onClick:()=>{E(null),R(m.id)},className:"w-full text-left px-3 py-1.5 text-red-600 hover:bg-red-50 flex items-center gap-2 transition-colors cursor-pointer",children:[e.jsx(Be,{size:13,className:"text-red-500"}),e.jsx("span",{children:t==="zh"?"卸载插件":"Uninstall"})]})]})]})]})]})]})]},m.id)})})})}function at(s,t){return s==null?null:s>=1e4?t==="zh"?`${(s/1e4).toFixed(1)} 万`:`${(s/1e3).toFixed(1)}k`:s>=1e3?t==="zh"?`${s}`:`${(s/1e3).toFixed(1)}k`:String(s)}function xs(s){if(s==null||!Number.isFinite(s)||s<=0)return null;if(s<1024)return`${Math.round(s)} B`;const t=s/1024;if(t<1024)return t>=100?`${Math.round(t)} KB`:`${t.toFixed(1)} KB`;const r=t/1024;return r>=100?`${Math.round(r)} MB`:`${r.toFixed(2)} MB`}function ms(s,t){if(!s)return null;const r=Date.parse(s);if(!Number.isFinite(r))return null;const i=new Date(r),c=t==="zh"?"zh-CN":"en-US",x=t==="zh"?`${i.getFullYear()}-${String(i.getMonth()+1).padStart(2,"0")}-${String(i.getDate()).padStart(2,"0")}`:i.toLocaleDateString(c,{year:"numeric",month:"short",day:"numeric"}),n=i.toLocaleString(c,{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"});return{short:x,full:n}}function Nt(s){const t=s.giteeRepoUrl??s.repository;if(!t||typeof t!="string")return null;try{const r=new URL(t.trim());if(r.protocol!=="https:"&&r.protocol!=="http:"||r.hostname.toLowerCase()!=="gitee.com")return null;const i=r.pathname.split("/").filter(Boolean);if(i.length<2)return null;const c=i[0],x=i[1].replace(/\.git$/i,"");return`https://gitee.com/${encodeURIComponent(c)}/${encodeURIComponent(x)}`}catch{return null}}const us="/api/plugins/community",wt="/api/plugins/community/config",hs="/api/plugins/community/gitee-star",ps="/api/plugins/community/gitee-stars",gs="/api/plugins/install-from-url",bs="/api/plugins/upload-zip-raw",Ye="https://gitee.com/aymwoo/openlearn-next-plugins/raw/master/community-plugins.json";function kt(s){return s?.databaseUrl??s?.source??s?.defaultUrl??Ye}const dt="openlearn.community.giteeToken",fs="https://gitee.com/profile/personal_access_tokens/new?scopes=projects";function ys(){try{return window.localStorage.getItem(dt)?.trim()??""}catch{return""}}function nt(s){try{s?window.localStorage.setItem(dt,s):window.localStorage.removeItem(dt)}catch{}}function js(s){try{const t=new URL(s).pathname.split("/").filter(Boolean).pop();return t?decodeURIComponent(t):"plugin.zip"}catch{return"plugin.zip"}}function vs(s){if(!s)return null;try{return new URL(s).host}catch{return null}}function Ns({plugin:s}){const[t,r]=o.useState(!1),i=s.icon?.trim(),c=!!i&&(i.startsWith("http://")||i.startsWith("https://"));return c&&!t?e.jsx("img",{src:i,alt:"",className:"w-11 h-11 rounded-lg object-cover border border-gray-100 shrink-0",onError:()=>r(!0)}):i&&!c?e.jsx("div",{className:"w-11 h-11 rounded-lg bg-indigo-50 border border-indigo-100 flex items-center justify-center text-xl shrink-0",children:e.jsx("span",{"aria-hidden":!0,children:i})}):e.jsx("div",{className:"w-11 h-11 rounded-lg bg-indigo-50 text-indigo-600 border border-indigo-100 flex items-center justify-center font-bold shrink-0",children:s.name.slice(0,1).toUpperCase()})}function ws(){return e.jsxs("div",{className:"bg-white rounded-xl border border-gray-200 p-5 shadow-sm flex flex-col gap-3 animate-pulse",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"w-11 h-11 rounded-lg bg-gray-100 shrink-0"}),e.jsxs("div",{className:"flex-1 space-y-2",children:[e.jsx("div",{className:"h-4 bg-gray-100 rounded w-2/3"}),e.jsx("div",{className:"h-3 bg-gray-100 rounded w-1/3"})]})]}),e.jsx("div",{className:"h-3 bg-gray-100 rounded"}),e.jsx("div",{className:"h-3 bg-gray-100 rounded w-5/6"}),e.jsx("div",{className:"h-8 bg-gray-100 rounded-lg mt-2"})]})}function ks({installedPlugins:s,lang:t,canConfigure:r,onInstalled:i,onRegistryLoaded:c,fetcher:x}){const n=t==="zh",g=o.useMemo(()=>x??((a,u)=>fetch(a,u)),[x]),T=o.useId(),b=o.useId(),O=o.useId(),D=o.useId(),G=o.useId(),v=o.useRef(null),[I,R]=o.useState("loading"),[y,N]=o.useState(null),[E,m]=o.useState(null),[l,j]=o.useState(""),[k,M]=o.useState(null),[p,f]=o.useState("recommended"),[F,Q]=o.useState(!1),[de,Ce]=o.useState({}),[U,ee]=o.useState({}),[ce,fe]=o.useState(!1),[se,Ee]=o.useState(Ye),[ue,ve]=o.useState(!1),[ae,te]=o.useState(null),[he,ye]=o.useState(null),[xe,ne]=o.useState(!1),[Ie,Ne]=o.useState(!1),[$e,Re]=o.useState(null),[W,H]=o.useState(null),[pe,Z]=o.useState({}),[we,ge]=o.useState(null),[Y,Le]=o.useState(()=>ys()),[Te,Ae]=o.useState(""),[Oe,oe]=o.useState(!1),[ie,S]=o.useState(null),[z,B]=o.useState(null),P=o.useId(),L=r??y?.canConfigure??!0,q=o.useCallback(async(a,u)=>{R("loading"),m(null);const h=u!==void 0?u:Y;try{const w=await g(`${us}${a?"?refresh=1":""}`,h?{headers:{"x-gitee-token":h}}:void 0),C=await w.json();if(!w.ok||!C?.success)throw new Error(C?.error||(n?"服务端拒绝请求":"Request rejected by the server"));N(C),Array.isArray(C.plugins)&&c?.(C.plugins),C.error?(ye(null),m(C.error),R("error")):R("ready")}catch(w){ye(null),m(w instanceof Error?w.message:String(w)),R("error")}},[g,Y,c,n]);o.useEffect(()=>{q(!1)},[q]),o.useEffect(()=>{!xe&&y&&Ee(kt(y))},[y,xe]);const Ue=o.useCallback(async a=>{try{const u=await g(`${ps}?pluginId=${encodeURIComponent(a)}`,Y?{headers:{"x-gitee-token":Y}}:void 0),h=await u.json().catch(()=>null);if(!u.ok||!h?.success)return;N(w=>w&&{...w,plugins:w.plugins.map(C=>C.id!==a?C:{...C,stars:typeof h.stars=="number"?h.stars:C.stars,starred:typeof h.starred=="boolean"?h.starred:C.starred,giteeRepoUrl:h.starUrl??C.giteeRepoUrl})})}catch{}},[g,Y]);o.useEffect(()=>{if(!z)return;const a=()=>{if(document.visibilityState&&document.visibilityState!=="visible")return;const u=z;B(null),Ue(u)};return window.addEventListener("focus",a),document.addEventListener("visibilitychange",a),()=>{window.removeEventListener("focus",a),document.removeEventListener("visibilitychange",a)}},[z,Ue]);const{installedManifestIds:Me,installedUuidByManifestId:V}=o.useMemo(()=>{const a=new Set,u=new Map;for(const h of s)try{const w=JSON.parse(h.manifest);typeof w?.id=="string"&&w.id?(a.add(w.id),u.set(w.id,h.id)):(a.add(h.id),u.set(h.id,h.id))}catch{a.add(h.id),u.set(h.id,h.id)}return{installedManifestIds:a,installedUuidByManifestId:u}},[s]),le=o.useCallback(a=>!!U[a.id]||Me.has(a.id)||a.installedVersion!==null,[Me,U]),_e=o.useMemo(()=>{const a=new Map;for(const u of y?.plugins??[])for(const h of u.tags)a.set(h,(a.get(h)??0)+1);return Array.from(a.entries()).sort((u,h)=>h[1]-u[1]||u[0].localeCompare(h[0])).slice(0,12).map(([u])=>u)},[y]),me=o.useMemo(()=>{const a=l.trim().toLowerCase();let u=(y?.plugins??[]).filter(h=>F&&le(h)&&!h.hasUpdate||k&&!h.tags.includes(k)?!1:a?[h.name,h.id,h.description,h.author,h.tags.join(" ")].join(" ").toLowerCase().includes(a):!0);return p==="latest"?u=[...u].sort((h,w)=>{const C=h.publishedAt?Date.parse(h.publishedAt):NaN,_=w.publishedAt?Date.parse(w.publishedAt):NaN;return(Number.isFinite(_)?_:-1)-(Number.isFinite(C)?C:-1)}):p==="stars"?u=[...u].sort((h,w)=>(w.stars??-1)-(h.stars??-1)):p==="downloads"?u=[...u].sort((h,w)=>(w.downloads??-1)-(h.downloads??-1)):p==="name"&&(u=[...u].sort((h,w)=>h.name.localeCompare(w.name))),u},[k,y,F,le,l,p]),Ve=o.useCallback(async a=>{if(!pe[a.id]){ge(null),Z(u=>({...u,[a.id]:!0}));try{const u=await g(hs,{method:"POST",headers:{"Content-Type":"application/json",...Y?{"x-gitee-token":Y}:{}},body:JSON.stringify({pluginId:a.id})}),h=await u.json().catch(()=>null);if(u.status===401&&h?.requiresGiteeAuth){h.reason==="invalid_token"&&Y&&(Le(""),nt(""));const _=h.starUrl||Nt(a)||"https://gitee.com";S({pluginId:a.id,pluginName:a.name,starUrl:_,message:h.error||(n?"需要登录并授权 Gitee 账号才可直接异步加星,已在新标签页打开该插件仓库":"Gitee login/authorization is required to star directly. Opened the repository in a new tab.")}),oe(!0),B(a.id);try{window.open(_,"_blank","noopener,noreferrer")}catch{}return}if(!u.ok||!h?.success||typeof h.starred!="boolean")throw new Error(h?.error||(n?"Gitee 标星操作失败":"Failed to update Gitee star"));S(_=>_?.pluginId===a.id?null:_);const w=h.starred,C=h.stars;N(_=>_&&{..._,plugins:_.plugins.map(X=>X.id!==a.id?X:{...X,starred:w,stars:typeof C=="number"?C:X.stars,giteeRepoUrl:h.starUrl??X.giteeRepoUrl})})}catch(u){ge(u instanceof Error?u.message:String(u))}finally{Z(u=>{const h={...u};return delete h[a.id],h})}}},[g,Y,pe,n]),qe=o.useCallback(async a=>{a.preventDefault();const u=Te.trim();if(u.length<8){ge(n?"请输入有效的 Gitee 私人令牌(至少 8 位)":"Please enter a valid Gitee token");return}ge(null),Le(u),nt(u),Ae(""),oe(!1),await q(!1,u)},[Te,q,n]),et=o.useCallback(async()=>{Le(""),nt(""),S(null),await q(!1,"")},[q]),De=o.useCallback((a,u)=>{Ce(h=>{const w={...h};return u?w[a]=u:delete w[a],w})},[]),He=o.useCallback(async(a,u)=>{De(a.id,{phase:"installing",message:n?"服务端下载不可用,改为浏览器直传…":"Server download unavailable, transferring from browser…"});const h=await g(a.downloadUrl);if(!h.ok)throw new Error(`${n?"浏览器下载失败":"Browser download failed"}: HTTP ${h.status}`);const w=await h.blob();if(w.size===0)throw new Error(n?"插件包为空":"The package is empty");const C=js(a.downloadUrl),_=le(a),X=V.get(a.id)??a.id,re=await g(bs,{method:"POST",headers:{"Content-Type":"application/octet-stream","x-filename":encodeURIComponent(C),"x-community-install":"true",..._?{"x-install-mode":"update","x-target-plugin-id":X}:{},...a.integrity?{"x-integrity":a.integrity}:{},...a.signature?{"x-signature":a.signature}:{},...u?{"x-approve-public-routes":"true"}:{}},body:w}),ke=await re.json().catch(()=>null);if(!re.ok||!ke?.success)throw re.status===403&&!ke?.code?new Error(n?"仅管理员可安装社区插件,请联系管理员。":"Only administrators can install plugins."):new Error(ke?.error||(n?"浏览器直传失败":"Browser upload failed"));return ke},[g,V,le,De,n]),Je=o.useCallback(async(a,u)=>{const h=le(a)&&a.hasUpdate;De(a.id,{phase:"installing",message:h?n?"正在创建更新前快照并下载新版本…":"Saving snapshot and downloading update…":n?"正在由服务端下载插件包…":"Server is downloading the package…"});try{const w=await g(gs,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({downloadUrl:a.downloadUrl,expectedId:a.id,approvePublicRoutes:u,...a.integrity?{integrity:a.integrity}:{},...a.signature?{signature:a.signature}:{}})}),C=await w.json().catch(()=>null);let _=!!C?.updated||h,X=C?.newVersion||a.version;if(!w.ok||!C?.success)if(C?.fallbackToClient){const re=await He(a,u);_=!!re?.updated||h,X=re?.newVersion||X}else throw w.status===403&&!C?.code?new Error(n?"仅管理员可安装社区插件,请联系管理员。":"Only administrators can install plugins."):new Error(C?.error||(n?h?"更新失败":"安装失败":h?"Update failed":"Installation failed"));ee(re=>({...re,[a.id]:!0})),N(re=>{if(!re)return re;const ke=re.plugins.map(tt=>tt.id===a.id?{...tt,installedVersion:X,hasUpdate:!1}:tt);return c?.(ke),{...re,plugins:ke}}),De(a.id,{phase:"success",message:_?n?`已更新至 v${X}(已自动保存更新前快照),正在刷新…`:`Updated to v${X} (pre-update snapshot saved), reloading…`:n?"安装完成,请在「发现」页启用":"Installed — enable it on the Discover tab"}),i?.()}catch(w){De(a.id,{phase:"error",message:w instanceof Error?w.message:String(w)})}},[g,le,i,c,De,He,n]),je=o.useCallback((a,u)=>{const h=a.trim();if(!h)return u?null:n?"请输入社区插件注册表 JSON 地址":"Please enter the community registry JSON URL";try{const w=new URL(h);if(w.protocol!=="http:"&&w.protocol!=="https:")return n?"仅支持 http:// 或 https:// 开头的公网地址":"Only http:// or https:// URLs are supported"}catch{return n?"请输入有效的 URL 地址(例如 https://example.com/community-plugins.json)":"Please enter a valid URL (e.g. https://example.com/community-plugins.json)"}return null},[n]),We=o.useCallback(()=>{Ee(kt(y)),te(null),ne(!1),fe(!0)},[y]),d=o.useCallback(async(a,u)=>{const h=a===null?"":a.trim(),w=je(h,u);if(w){te(w),v.current?.focus();return}ve(!0),te(null),ye(null);try{const C=await g(wt,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({registryUrl:h||null})}),_=await C.json().catch(()=>null);if(!C.ok||!_?.success){const X=C.status===403?n?"仅管理员可修改社区插件源设置":"Only administrators can configure the community registry":_?.error||(n?"保存社区插件源失败":"Failed to save community registry URL");te(X),v.current?.focus();return}fe(!1),ne(!1),Ee(_?.activeUrl||h||_?.defaultUrl||y?.defaultUrl||Ye),ye(h?n?"社区插件源已保存,正在加载注册表…":"Community registry URL saved, loading…":y?.hasEnvFallback?n?"已清除界面自定义插件源,正在恢复环境变量配置…":"Cleared custom community registry URL":n?"已恢复官方默认插件源,正在加载…":"Restored official default registry, loading…"),await q(!0)}catch(C){te(C instanceof Error?C.message:String(C))}finally{ve(!1)}},[y?.defaultUrl,y?.hasEnvFallback,g,q,je,n]),$=o.useCallback(async a=>{if(L){Ne(!0),Re(null),H(null);try{const u=await g(wt,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({installEnabled:a})}),h=await u.json().catch(()=>null);if(!u.ok||!h?.success){const w=u.status===403?n?"仅管理员可修改社区安装开关":"Only administrators can change the community install switch":h?.error||(n?"保存社区安装开关失败":"Failed to save community install setting");Re(w);return}H(a===null?n?"已清除界面覆盖,将按环境变量或默认开启生效":"Cleared UI override; env or default-on now applies":a?n?"已允许从社区注册表安装,立即生效":"Community registry install enabled; takes effect immediately":n?"已关闭社区注册表安装,立即生效":"Community registry install disabled; takes effect immediately"),await q(!1)}catch(u){Re(u instanceof Error?u.message:String(u))}finally{Ne(!1)}}},[L,g,q,n]),A=(a={})=>{const u=!!(y?.installEnabled??!0),h=L?y?.installConfigSource:null,w=L?y?.storedInstallEnabled:null,C=L?!!y?.hasInstallEnvFallback:!1,_=h==="database"?n?"当前来源:管理员界面配置(覆盖环境变量)":"Source: admin UI setting (overrides env)":h==="env"?n?`当前来源:环境变量 OPENLEARN_COMMUNITY_INSTALL=${u?"1":"0"}(未在界面显式配置)`:`Source: OPENLEARN_COMMUNITY_INSTALL=${u?"1":"0"} (no explicit UI setting)`:h==="default"?n?"当前来源:默认开启(未在界面或环境变量中关闭)":"Source: default on (not disabled in UI or env)":null;return e.jsxs("div",{className:a.compact?"mb-4 px-4 py-3 rounded-xl bg-white border border-gray-200 text-left":"rounded-lg border border-gray-200 bg-slate-50/70 px-3 py-3 space-y-2","data-testid":"community-install-toggle",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("p",{className:"text-xs font-semibold text-gray-800",children:n?"允许从社区注册表安装":"Allow install from community registry"}),e.jsx("p",{className:"text-xs text-gray-500 leading-relaxed mt-1",children:L?n?"默认开启。管理员可在此开启或关闭,保存后立即对安装、URL 更新和社区直传生效。未在界面显式配置时,可使用环境变量 OPENLEARN_COMMUNITY_INSTALL=0 关闭;显式设置会覆盖该环境变量。":"On by default. Admins can turn this on or off here; the change applies immediately to install, URL update, and community direct upload. When no explicit UI value is stored, env OPENLEARN_COMMUNITY_INSTALL=0 can disable it; an explicit UI value overrides that env.":n?"社区安装由管理员在插件中心配置,默认开启。":"Community install is configured by an administrator and is on by default."}),e.jsxs("p",{className:"text-xs text-gray-600 mt-1.5",children:[n?`有效状态:${u?"已开启":"已关闭"}`:`Effective: ${u?"on":"off"}`,_?` · ${_}`:""]})]}),e.jsx("button",{type:"button",role:"switch","aria-checked":u,"aria-label":n?"允许从社区注册表安装":"Allow install from community registry",disabled:!L||Ie,onClick:()=>void $(!u),className:`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors ${u?"bg-indigo-600":"bg-gray-300"} ${!L||Ie?"opacity-60 cursor-not-allowed":"cursor-pointer"}`,children:e.jsx("span",{className:`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${u?"translate-x-6":"translate-x-1"}`})})]}),L&&w!==null&&w!==void 0&&e.jsx("button",{type:"button",disabled:Ie,onClick:()=>void $(null),className:"text-xs font-semibold text-indigo-700 hover:text-indigo-900 disabled:opacity-60 cursor-pointer",children:C?n?"清除界面覆盖,回退到环境变量":"Clear UI override and fall back to env":n?"清除界面覆盖,恢复默认开启":"Clear UI override and fall back to default on"}),$e&&e.jsx("p",{role:"alert",className:"text-xs text-rose-600",children:$e}),W&&!$e&&e.jsx("p",{role:"status",className:"text-xs text-emerald-700",children:W})]})},J=(a={})=>{const u=!!a.isUnconfiguredCard,h=!!y?.databaseUrl||y?.configSource==="database",w=!u&&h,C=ae?`${b} ${O}`:b;return e.jsxs("form",{"aria-labelledby":u?void 0:G,noValidate:!0,onSubmit:_=>{_.preventDefault(),d(se,w)},className:u?"mt-6 text-left":"space-y-3",children:[e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{htmlFor:T,className:"text-xs font-semibold text-gray-700",children:n?"社区插件注册表 JSON 地址":"Community Registry JSON URL"}),e.jsx("p",{id:b,className:"text-xs text-gray-500 leading-relaxed",children:n?`支持远端 JSON 注册表地址(仅允许公网 HTTP(S),回环与内网地址会被安全拦截)。${!u&&h?y?.hasEnvFallback?`留空保存或点击清除可恢复环境变量 ${y?.envVar||"PLUGIN_COMMUNITY_REGISTRY_URL"} 默认配置。`:"留空保存或点击恢复可切回官方默认插件源。":""}`:`Enter a remote JSON registry URL (public HTTP(S) only; loopback and private networks are blocked).${!u&&h?y?.hasEnvFallback?` Clear to fall back to ${y?.envVar||"PLUGIN_COMMUNITY_REGISTRY_URL"}.`:" Clear to restore the official default registry.":""}`}),e.jsx("input",{ref:v,id:T,name:"registryUrl",type:"url",value:se,onChange:_=>{Ee(_.target.value),ne(!0),ae&&te(null)},onBlur:()=>{xe&&te(je(se,w))},placeholder:y?.defaultUrl||Ye,autoComplete:"url",autoCorrect:"off",autoCapitalize:"none",spellCheck:!1,enterKeyHint:"done","aria-invalid":!!ae,"aria-describedby":C,className:`w-full px-3 py-2 text-sm bg-white border rounded-lg focus:outline-none focus:ring-2 ${ae?"border-rose-400 focus:ring-rose-500/30 focus:border-rose-500":"border-gray-200 focus:ring-indigo-500/30 focus:border-indigo-400"}`}),ae&&e.jsxs("p",{id:O,role:"alert",className:"text-xs text-rose-600 flex items-center gap-1.5 mt-0.5",children:[e.jsx(Pe,{size:12,className:"shrink-0"}),e.jsx("span",{children:ae})]})]}),A(),e.jsxs("div",{className:"flex flex-wrap items-center gap-2 pt-1",children:[e.jsxs("button",{type:"submit",disabled:ue,className:"inline-flex items-center gap-1.5 px-3.5 py-2 text-xs font-semibold rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 disabled:opacity-60 transition-colors cursor-pointer",children:[ue&&e.jsx(K,{size:13,className:"animate-spin"}),e.jsx("span",{children:u?n?"保存并加载":"Save & Load":n?"保存并刷新":"Save & Refresh"})]}),!u&&h&&e.jsx("button",{type:"button",disabled:ue,onClick:()=>void d(null,!0),className:"px-3 py-2 text-xs font-semibold rounded-lg border border-gray-200 bg-white text-gray-600 hover:bg-gray-50 hover:text-gray-900 disabled:opacity-60 transition-colors cursor-pointer",children:y?.hasEnvFallback?n?"恢复环境变量默认":"Reset to env default":n?"恢复官方默认":"Reset to official default"}),!u&&e.jsx("button",{type:"button",disabled:ue,onClick:()=>{fe(!1),te(null),ne(!1)},className:"px-3 py-2 text-xs font-medium rounded-lg text-gray-500 hover:text-gray-800 hover:bg-gray-100 transition-colors cursor-pointer",children:n?"收起":"Close"})]})]})};return I==="ready"&&y&&!y.configured?e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-gray-50/30",children:e.jsxs("div",{className:"max-w-2xl mx-auto mt-10 bg-white border border-gray-200 rounded-2xl p-8 text-center shadow-sm",children:[e.jsx(ot,{size:44,className:"mx-auto text-indigo-300 mb-4"}),e.jsx("h3",{className:"text-lg font-bold text-gray-900",children:n?"尚未配置社区插件源":"No community registry configured"}),e.jsx("p",{className:"text-sm text-gray-500 mt-2 leading-relaxed",children:L?n?"社区市场会从一个远端 JSON 注册表读取可安装插件。您可以直接在下方配置注册表地址,或在服务端环境变量中设置:":"The community market reads an installable-plugin list from a remote JSON registry. Configure the registry URL below or set the server environment variable:":n?"社区市场会从一个远端 JSON 注册表读取可安装插件。请联系管理员在页面中配置地址,或在服务端环境变量中配置后重启服务:":"The community market reads an installable-plugin list from a remote JSON registry. Ask an administrator to configure it or set the environment variable on the server:"}),L&&J({isUnconfiguredCard:!0}),!L&&A({compact:!0}),e.jsxs("div",{className:"mt-5 pt-4 border-t border-gray-100",children:[e.jsx("p",{className:"text-xs text-gray-400 mb-2",children:n?"也可以通过服务端环境变量配置:":"Or configure via server environment variable:"}),e.jsxs("code",{className:"inline-block px-3 py-1.5 rounded-lg bg-gray-900 text-gray-50 text-xs font-mono",children:[y.envVar||"PLUGIN_COMMUNITY_REGISTRY_URL","=","<https://example.com/community-plugins.json>"]})]}),!L&&e.jsx("p",{className:"text-xs text-gray-400 mt-4",children:n?"地址仅允许公网 HTTP(S),回环与内网地址会被安全拦截。":"Only public HTTP(S) endpoints are allowed; loopback and private addresses are blocked."})]})}):e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 bg-gray-50/30",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 mb-4",children:[e.jsxs("div",{className:"relative flex-1 min-w-[220px]",children:[e.jsx(Gt,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"}),e.jsx("input",{type:"text",value:l,onChange:a=>j(a.target.value),placeholder:n?"搜索插件名称、作者或标签…":"Search by name, author or tag…","aria-label":n?"搜索社区插件":"Search community plugins",className:"w-full pl-9 pr-8 py-2 text-sm bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500/30 focus:border-indigo-300"}),l&&e.jsx("button",{type:"button",onClick:()=>j(""),"aria-label":n?"清空搜索":"Clear search",className:"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 cursor-pointer",children:e.jsx(Se,{size:14})})]}),e.jsxs("select",{value:p,onChange:a=>f(a.target.value),"aria-label":n?"排序方式":"Sort order",className:"px-3 py-2 text-sm bg-white border border-gray-200 rounded-lg text-gray-600 cursor-pointer focus:outline-none focus:ring-2 focus:ring-indigo-500/30",children:[e.jsx("option",{value:"recommended",children:n?"推荐排序":"Recommended"}),e.jsx("option",{value:"latest",children:n?"最新发布":"Latest release"}),e.jsx("option",{value:"stars",children:n?"最多标星":"Most starred"}),e.jsx("option",{value:"downloads",children:n?"下载最多":"Most downloaded"}),e.jsx("option",{value:"name",children:n?"按名称":"By name"})]}),e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer text-xs font-semibold text-gray-500 hover:text-gray-900 select-none transition-colors border border-gray-200 rounded-lg px-3 py-2 bg-white shadow-sm",children:[e.jsx("input",{type:"checkbox",checked:F,onChange:a=>Q(a.target.checked),className:"w-3.5 h-3.5 rounded text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer"}),e.jsx("span",{children:n?"隐藏已安装":"Hide installed"})]}),e.jsxs("button",{type:"button",onClick:()=>void q(!0),disabled:I==="loading",className:"flex items-center gap-1.5 text-xs font-semibold border border-gray-200 rounded-lg px-3 py-2 bg-white shadow-sm text-gray-600 hover:text-gray-900 hover:border-gray-300 transition-colors disabled:opacity-50 cursor-pointer",children:[I==="loading"?e.jsx(K,{size:14,className:"animate-spin"}):e.jsx(Fe,{size:14}),e.jsx("span",{children:n?"刷新":"Refresh"})]}),e.jsxs("button",{type:"button","data-testid":"community-gitee-auth-btn",onClick:()=>oe(a=>!a),"aria-expanded":Oe,className:`flex items-center gap-1.5 text-xs font-semibold border rounded-lg px-3 py-2 shadow-sm transition-colors cursor-pointer ${Y?"border-amber-300 bg-amber-50 text-amber-800 hover:bg-amber-100":"border-gray-200 bg-white text-gray-600 hover:text-gray-900 hover:border-gray-300"}`,children:[e.jsx(ht,{size:14}),e.jsx("span",{children:Y?n?"已绑定 Gitee":"Gitee Linked":n?"Gitee 授权":"Gitee Auth"})]}),L&&e.jsxs("button",{type:"button",onClick:()=>{ce?(fe(!1),te(null)):We()},"aria-expanded":ce,"aria-controls":D,className:`flex items-center gap-1.5 text-xs font-semibold border rounded-lg px-3 py-2 shadow-sm transition-colors cursor-pointer ${ce?"border-indigo-300 bg-indigo-50 text-indigo-700":"border-gray-200 bg-white text-gray-600 hover:text-gray-900 hover:border-gray-300"}`,children:[e.jsx(Ze,{size:14}),e.jsx("span",{children:n?"插件源设置":"Registry Source"})]})]}),he&&e.jsxs("div",{role:"status","aria-live":"polite",className:"mb-4 px-4 py-2.5 rounded-xl bg-emerald-50 border border-emerald-200 text-xs text-emerald-700 flex items-center justify-between gap-2",children:[e.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[e.jsx(Ge,{size:14,className:"shrink-0"}),e.jsx("span",{children:he})]}),e.jsx("button",{type:"button",onClick:()=>ye(null),"aria-label":n?"关闭提示":"Dismiss notice",className:"text-emerald-600 hover:text-emerald-900 cursor-pointer",children:e.jsx(Se,{size:13})})]}),L&&ce&&e.jsxs("section",{id:D,"data-testid":"community-registry-config-panel",className:"mb-4 bg-white border border-indigo-200 rounded-xl p-4 shadow-sm",children:[e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2 mb-3",children:[e.jsxs("h3",{id:G,className:"text-sm font-bold text-gray-900 flex items-center gap-1.5",children:[e.jsx(Ze,{size:14,className:"text-indigo-600"}),e.jsx("span",{children:n?"社区插件源设置":"Community Registry Settings"})]}),e.jsx("span",{className:"text-xs px-2 py-0.5 rounded-full bg-slate-100 text-slate-600 font-medium",children:y?.configSource==="database"?y?.hasEnvFallback?n?"当前来源:界面配置(覆盖环境变量)":"Source: UI setting (overrides env)":n?"当前来源:界面配置(覆盖官方默认)":"Source: UI setting (overrides default)":y?.configSource==="env"?n?`当前来源:环境变量 ${y.envVar||"PLUGIN_COMMUNITY_REGISTRY_URL"}`:`Source: env ${y.envVar||"PLUGIN_COMMUNITY_REGISTRY_URL"}`:y?.configSource==="default"?n?"当前来源:官方默认(Gitee)":"Source: official default (Gitee)":n?"当前状态:未配置":"Status: Not configured"})]}),J()]}),we&&e.jsxs("div",{role:"alert",className:"mb-4 px-4 py-2.5 rounded-xl bg-rose-50 border border-rose-200 text-xs text-rose-700 flex items-center justify-between gap-2",children:[e.jsx("span",{children:we}),e.jsx("button",{type:"button",onClick:()=>ge(null),"aria-label":n?"关闭提示":"Dismiss notice",className:"text-rose-500 hover:text-rose-800 cursor-pointer",children:e.jsx(Se,{size:13})})]}),(ie||Oe)&&e.jsxs("section",{"data-testid":"community-gitee-auth-panel",className:"mb-4 px-4 py-3.5 rounded-xl bg-amber-50/90 border border-amber-200 text-xs text-amber-900 space-y-2.5",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"space-y-1 flex-1 min-w-0",children:[e.jsxs("p",{className:"font-semibold flex items-center gap-1.5",children:[e.jsx(ht,{size:13,className:"text-amber-700 shrink-0"}),e.jsx("span",{children:ie?ie.message:n?"Gitee 账号令牌授权(用于页面内一键异步加⭐)":"Gitee Personal Access Token (for 1-click async starring)"})]}),ie&&e.jsxs("p",{className:"text-amber-800 leading-relaxed",children:[n?e.jsxs(e.Fragment,{children:["已打开新标签页前往「",ie.pluginName,"」Gitee 仓库。若浏览器拦截了弹窗,请直接点击:"]}):e.jsxs(e.Fragment,{children:["Opened a new tab for “",ie.pluginName,"”. If popups were blocked, open directly:"]})," ",e.jsx("a",{href:ie.starUrl,target:"_blank",rel:"noopener noreferrer",className:"font-semibold underline text-amber-900 hover:text-amber-950",children:ie.starUrl})]})]}),e.jsx("button",{type:"button",onClick:()=>{S(null),oe(!1)},"aria-label":n?"关闭 Gitee 授权提示":"Dismiss Gitee auth prompt",className:"text-amber-700 hover:text-amber-950 cursor-pointer shrink-0",children:e.jsx(Se,{size:14})})]}),e.jsxs("form",{onSubmit:a=>void qe(a),className:"flex flex-wrap items-end gap-2",children:[e.jsxs("div",{className:"flex-1 min-w-[220px]",children:[e.jsx("label",{htmlFor:P,className:"block text-[11px] font-medium text-amber-800 mb-1",children:n?"可选:绑定你的 Gitee 私人令牌(仅存当前浏览器,勾选 projects 权限),绑定后点击 +⭐ 无需跳出页面":"Optional: bind a Gitee personal access token (stored in your browser only, projects scope)"}),e.jsx("input",{id:P,type:"password",value:Te,onChange:a=>Ae(a.target.value),placeholder:n?"粘贴 Gitee 私人令牌…":"Paste your Gitee personal access token…",className:"w-full px-2.5 py-1.5 text-xs bg-white border border-amber-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-amber-400/50"})]}),e.jsx("button",{type:"submit",className:"px-3 py-1.5 text-xs font-semibold rounded-lg bg-amber-600 text-white hover:bg-amber-700 transition-colors cursor-pointer",children:n?"保存令牌":"Save token"}),e.jsx("a",{href:fs,target:"_blank",rel:"noopener noreferrer",className:"px-2.5 py-1.5 text-xs font-medium rounded-lg border border-amber-300 bg-white text-amber-800 hover:bg-amber-100 transition-colors",children:n?"去 Gitee 创建令牌":"Create Gitee token"}),Y&&e.jsx("button",{type:"button",onClick:()=>void et(),className:"px-2.5 py-1.5 text-xs font-medium rounded-lg border border-rose-200 bg-white text-rose-700 hover:bg-rose-50 transition-colors cursor-pointer",children:n?"解除绑定":"Clear token"})]})]}),!ce&&A({compact:!0}),y?.source&&I==="ready"&&e.jsxs("div",{className:"flex flex-wrap items-center gap-3 mb-4 text-xs text-gray-400",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(ot,{size:11}),e.jsx("span",{className:"font-mono",children:vs(y.source)})]}),e.jsx("span",{children:n?`${me.length} / ${y.plugins.length} 个插件`:`${me.length} of ${y.plugins.length} plugins`}),y.fetchedAt&&e.jsxs("span",{className:"font-mono",children:[n?"更新于 ":"Fetched ",new Date(y.fetchedAt).toLocaleTimeString(n?"zh-CN":"en-US",{hour:"2-digit",minute:"2-digit"})]}),y.skipped>0&&e.jsxs("span",{title:n?"注册表中存在格式非法或地址不安全的条目":"Registry entries with invalid format or unsafe URLs",children:["⚠️ ",n?`${y.skipped} 条记录被忽略`:`${y.skipped} entries ignored`]})]}),_e.length>0&&I==="ready"&&e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5 mb-4","data-testid":"community-tag-filters",children:[e.jsx("button",{type:"button",onClick:()=>M(null),className:`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors cursor-pointer ${k===null?"bg-indigo-600 border-indigo-600 text-white":"bg-white border-gray-200 text-gray-500 hover:border-indigo-300 hover:text-indigo-600"}`,children:n?"全部":"All"}),_e.map(a=>e.jsx("button",{type:"button",onClick:()=>M(k===a?null:a),className:`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors cursor-pointer ${k===a?"bg-indigo-600 border-indigo-600 text-white":"bg-white border-gray-200 text-gray-500 hover:border-indigo-300 hover:text-indigo-600"}`,children:a},a))]}),I==="error"&&e.jsxs("div",{className:"bg-amber-50 border border-amber-200 rounded-xl p-5 flex items-start gap-3",role:"alert",children:[e.jsx(Pe,{size:18,className:"text-amber-600 shrink-0 mt-0.5"}),e.jsxs("div",{className:"flex-1",children:[e.jsx("p",{className:"text-sm font-semibold text-amber-800",children:n?"社区插件市场加载失败":"Failed to load the community market"}),e.jsx("p",{className:"text-xs text-amber-700 mt-1 break-all",children:E}),e.jsxs("div",{className:"mt-3 flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:()=>void q(!0),className:"px-3 py-1.5 text-xs font-semibold rounded-lg bg-amber-600 text-white hover:bg-amber-700 transition-colors cursor-pointer",children:n?"重试":"Retry"}),L&&!ce&&e.jsx("button",{type:"button",onClick:We,className:"px-3 py-1.5 text-xs font-semibold rounded-lg border border-amber-300 bg-white text-amber-800 hover:bg-amber-100 transition-colors cursor-pointer",children:n?"修改插件源":"Edit registry source"})]})]})]}),I==="loading"&&e.jsx("div",{className:"grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 gap-4",children:Array.from({length:6},(a,u)=>e.jsx(ws,{},u))}),I==="ready"&&me.length===0&&e.jsxs("div",{className:"text-center py-16",children:[e.jsx(Bt,{size:44,className:"mx-auto text-gray-300 mb-3"}),e.jsx("p",{className:"text-sm font-medium text-gray-500",children:y?.plugins.length?n?"没有符合当前筛选条件的插件":"No plugins match the current filters":n?"社区注册表暂时没有可用插件":"The community registry has no plugins yet"}),y?.plugins.length?e.jsx("button",{type:"button",onClick:()=>{j(""),M(null),Q(!1)},className:"mt-3 text-xs font-semibold text-indigo-600 hover:text-indigo-800 cursor-pointer",children:n?"清除筛选条件":"Clear filters"}):null]}),I==="ready"&&me.length>0&&e.jsx("div",{className:"grid grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 gap-4",children:me.map(a=>{const u=de[a.id],h=le(a),w=ms(a.publishedAt,t),C=xs(a.packageSize),_=!!pe[a.id],X=Nt(a),re=h?a.hasUpdate?n?`更新至 v${a.version}`:`Update to v${a.version}`:n?"已安装":"Installed":n?"安装":"Install";return e.jsxs("article",{"data-testid":`community-card-${a.id}`,className:"bg-white rounded-xl border border-gray-200 p-5 shadow-sm hover:shadow-md transition-all flex flex-col gap-3 relative overflow-hidden",children:[e.jsxs("div",{className:"absolute top-0 right-0 p-3 flex items-center gap-1.5 flex-wrap justify-end",children:[a.featured&&e.jsxs("span",{className:"text-xs font-bold px-2 py-0.5 rounded-full bg-gradient-to-r from-amber-500 to-orange-500 text-white flex items-center gap-1 shadow-sm",children:[e.jsx(xt,{size:9}),n?"精选":"FEATURED"]}),a.verified&&e.jsxs("span",{title:n?"官方安全社区插件":"Official safe community plugin",className:"text-xs font-bold bg-emerald-50 text-emerald-700 border border-emerald-200 px-1.5 py-0.5 rounded-full flex items-center gap-0.5",children:[e.jsx(Vt,{size:11}),n?"已认证":"VERIFIED"]})]}),e.jsxs("div",{className:"flex items-start gap-3 pr-20",children:[e.jsx(Ns,{plugin:a}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h4",{className:"font-bold text-gray-900 truncate",title:a.name,children:a.name}),e.jsxs("div",{className:"flex items-center gap-1.5 mt-0.5 text-xs text-gray-400 min-w-0",children:[e.jsx("span",{className:"truncate",children:a.author}),a.version&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-gray-300",children:"·"}),e.jsxs("span",{className:"font-mono shrink-0",children:["v",a.version]})]})]})]})]}),e.jsx("p",{className:"text-sm text-gray-600 leading-relaxed line-clamp-3 min-h-[3.6rem]",children:a.description||(n?"该插件未提供描述。":"No description provided.")}),(a.tags.length>0||a.capabilities.length>0)&&e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[a.tags.slice(0,4).map(ke=>e.jsx("span",{className:"text-xs font-medium bg-slate-50 text-slate-600 border border-slate-200 px-2 py-0.5 rounded-full",children:ke},ke)),a.capabilities.length>0&&e.jsxs("span",{title:a.capabilities.join(`
|
|
3
|
+
`),className:"inline-flex items-center gap-1 text-xs font-medium bg-rose-50 text-rose-600 border border-rose-100 px-2 py-0.5 rounded-full",children:[e.jsx(ct,{size:9}),n?`${a.capabilities.length} 项权限`:`${a.capabilities.length} permission${a.capabilities.length>1?"s":""}`]}),h&&a.installedVersion&&e.jsxs("span",{className:"inline-flex items-center gap-1 text-xs font-medium bg-indigo-50 text-indigo-700 border border-indigo-100 px-2 py-0.5 rounded-full",children:[n?"已装":"Installed"," v",a.installedVersion]})]}),(w||C)&&e.jsxs("div",{"data-testid":`community-release-meta-${a.id}`,className:"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-gray-500",children:[w&&e.jsxs("span",{className:"inline-flex items-center gap-1",title:n?`发布时间: ${w.full}`:`Released: ${w.full}`,children:[e.jsx(Ht,{size:11,className:"text-gray-400 shrink-0"}),e.jsx("time",{dateTime:a.publishedAt??void 0,children:n?`发布于 ${w.short}`:`Released ${w.short}`})]}),C&&e.jsxs("span",{className:"inline-flex items-center gap-1 font-mono",title:n?`ZIP 包大小: ${C}`:`ZIP package size: ${C}`,children:[e.jsx(Ct,{size:11,className:"text-gray-400 shrink-0"}),e.jsx("span",{children:C})]})]}),e.jsxs("div",{className:"mt-auto flex flex-col gap-2 pt-1",children:[e.jsxs("div",{className:"flex items-center justify-between text-xs text-gray-400 border-t border-gray-100 pt-2 gap-2 flex-wrap",children:[e.jsxs("div",{className:"flex items-center gap-2.5 min-w-0 flex-wrap",children:[a.downloads!==null&&e.jsxs("span",{className:"inline-flex items-center gap-1",title:n?"下载量":"Downloads",children:[e.jsx(Qe,{size:10}),at(a.downloads,t)]}),a.license&&e.jsx("span",{className:"font-mono",children:a.license})]}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[ze(a.repository)&&e.jsxs("a",{href:ze(a.repository),target:"_blank",rel:"noopener noreferrer",title:n?"查看源码仓库":"View source repository",className:"inline-flex items-center gap-1 text-xs font-mono text-indigo-600 hover:text-indigo-800 bg-indigo-50 hover:bg-indigo-100 px-1.5 py-0.5 rounded border border-indigo-200/60 transition-colors",children:[e.jsx(mt,{size:10}),e.jsx("span",{children:n?"源码":"Code"})]}),ze(a.homepage)&&e.jsx("a",{href:ze(a.homepage),target:"_blank",rel:"noopener noreferrer",title:n?"打开主页":"Open homepage",className:"inline-flex items-center gap-1 text-xs text-gray-500 hover:text-gray-800 bg-gray-50 hover:bg-gray-100 px-1.5 py-0.5 rounded border border-gray-200 transition-colors",children:e.jsx(it,{size:10})})]})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("button",{type:"button",onClick:()=>{Je(a,!0)},disabled:u?.phase==="installing"||h&&!a.hasUpdate,className:`px-3 py-1.5 text-xs font-semibold rounded-lg transition-all flex items-center gap-1.5 shrink-0 disabled:cursor-default ${h&&!a.hasUpdate?"bg-emerald-50 text-emerald-700 border border-emerald-200":"bg-gradient-to-r from-indigo-600 to-violet-600 text-white hover:from-indigo-700 hover:to-violet-700 shadow-sm disabled:opacity-60 cursor-pointer"}`,children:[u?.phase==="installing"?e.jsx(K,{size:12,className:"animate-spin"}):h&&!a.hasUpdate?e.jsx(Ge,{size:12}):e.jsx(Qe,{size:12}),e.jsx("span",{children:re})]}),X?e.jsxs("button",{type:"button","data-testid":`community-star-${a.id}`,"aria-pressed":!!a.starred,disabled:_,onClick:()=>void Ve(a),title:a.starred?n?"取消 Gitee 仓库标星":"Unstar Gitee repo":n?"为 Gitee 仓库加⭐":"Star Gitee repo (+⭐)",className:`inline-flex items-center rounded-lg border text-xs font-semibold transition-colors shrink-0 cursor-pointer disabled:opacity-60 ${a.starred?"bg-amber-50 border-amber-300 text-amber-800 hover:bg-amber-100":"bg-white border-gray-200 text-gray-700 hover:bg-gray-50 hover:border-gray-300"}`,children:[e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1.5",children:[_?e.jsx(K,{size:12,className:"animate-spin text-amber-500"}):e.jsx(pt,{size:12,className:a.starred?"fill-amber-400 text-amber-500":"text-gray-400"}),e.jsx("span",{children:a.starred?n?"已⭐":"Starred":"+⭐"})]}),e.jsx("span",{className:`px-2 py-1.5 border-l font-mono ${a.starred?"border-amber-200 bg-amber-100/60":"border-gray-200 bg-gray-50"}`,children:at(a.stars??0,t)})]}):a.stars!==null&&e.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1.5 rounded-lg border border-gray-200 bg-gray-50 text-xs font-mono text-gray-600 shrink-0",title:n?"仓库 ⭐ 数":"Repository stars",children:[e.jsx(pt,{size:12,className:"text-amber-400 fill-amber-400"}),e.jsx("span",{children:at(a.stars,t)})]}),u&&e.jsx("span",{className:`text-xs ${u.phase==="error"?"text-rose-600 break-words flex-1 min-w-[12rem]":u.phase==="success"?"text-emerald-600 truncate":"text-gray-400 truncate"}`,title:u.message,role:u.phase==="error"?"alert":void 0,children:u.message})]})]})]},a.id)})})]})}function Ss(s){if(!s||typeof s!="string")return null;const t=Kt(s);return t.manifest?{manifest:t.manifest}:null}const zs=`exports.default = {
|
|
4
|
+
manifest: {
|
|
5
|
+
id: "ext-ai-quiz-gen",
|
|
6
|
+
name: "AI Course Quiz Generator (智能测验生成器)",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
description: "自动根据教案主题或特定文本,一键生成结构化的单选/多选随堂测验,并直接分发至指定班级的学生学习终端。",
|
|
9
|
+
author: "Openlearn Next AI Team",
|
|
10
|
+
capabilitiesProposed: ["lesson:read", "whiteboard:write"]
|
|
11
|
+
},
|
|
12
|
+
activate: async (ctx) => {
|
|
13
|
+
ctx.actionRegistry.register({
|
|
14
|
+
id: 'ext-quiz-create',
|
|
15
|
+
commandType: 'quiz.create',
|
|
16
|
+
description: '为指定课时快速创建一份包含指定题目的随堂测验卷,可直接投射至白板或学生端',
|
|
17
|
+
capabilityRequired: 'lesson:read',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'OBJECT',
|
|
20
|
+
properties: {
|
|
21
|
+
lessonId: { type: 'STRING', description: '关联的课时 ID' },
|
|
22
|
+
title: { type: 'STRING', description: '测验标题' },
|
|
23
|
+
questionsCount: { type: 'INTEGER', description: '自动生成的题目数量 (1-10)' }
|
|
24
|
+
},
|
|
25
|
+
required: ['lessonId', 'title']
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
ctx.commandBus.registerHandler('quiz.create', {
|
|
30
|
+
execute: async (command) => {
|
|
31
|
+
const payload = command.payload;
|
|
32
|
+
const count = payload.questionsCount || 3;
|
|
33
|
+
|
|
34
|
+
const mockQuestions = Array.from({ length: count }).map((_, i) => ({
|
|
35
|
+
id: 'q_' + (i + 1),
|
|
36
|
+
question: \`关于\${payload.title}的第 \${i + 1} 道随堂思考题?\`,
|
|
37
|
+
options: ["选项 A: 基础概念描述", "选项 B: 核心定理应用", "选项 C: 边界条件推导", "选项 D: 实验验证方法"],
|
|
38
|
+
answerIndex: i % 4
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
const quizData = {
|
|
42
|
+
quizId: 'qz_' + Math.random().toString(36).slice(2, 9),
|
|
43
|
+
lessonId: payload.lessonId,
|
|
44
|
+
title: payload.title,
|
|
45
|
+
questions: mockQuestions,
|
|
46
|
+
createdTime: new Date().toISOString()
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
await ctx.eventBus.publish({
|
|
50
|
+
id: 'evt_' + Math.random().toString(36).slice(2),
|
|
51
|
+
type: 'quiz.created',
|
|
52
|
+
source: 'plugin.ext-ai-quiz-gen',
|
|
53
|
+
payload: quizData,
|
|
54
|
+
timestamp: Date.now()
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
quiz: quizData,
|
|
60
|
+
message: \`已为课时 \${payload.lessonId} 成功生成包含 \${count} 道题目的测验包!\`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};`;function Cs({lang:s,pluginCode:t,setPluginCode:r,plugins:i,installingPlugin:c,onInstall:x,onToggle:n,onDelete:g,setUpdateTargetPluginId:T,updateFileInputRef:b,setStoreTab:O,zipError:D,setZipError:G,zipPreview:v,setZipPreview:I,zipProcessing:R,hasLegacyPlugins:y,dismissMigration:N,MigrationPromptBanner:E}){return e.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden bg-gray-950",children:[e.jsxs("div",{className:"p-4 bg-gray-900 border-b border-gray-800 flex justify-between items-center shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(st,{size:16,className:"text-amber-400"}),e.jsxs("div",{children:[e.jsx("p",{className:"text-xs font-semibold text-gray-200",children:s==="zh"?"开发者工具: 插件旁路加载与实时 Manifest 校验":"Developer Tools: Plugin Sideloading & Real-time Manifest Validation"}),e.jsx("p",{className:"text-xs text-gray-505",children:s==="zh"?"在安装前系统将进行解析、安全授权与注册接口预览机制":"Parse metadata, proposed permissions, and registered triggers before installation"})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",className:`inline-flex items-center gap-1.5 px-2.5 py-1 text-xs uppercase font-bold rounded-lg cursor-pointer transition-all ${D?"border border-red-500/30 bg-red-500/10 text-red-400 hover:bg-red-500/20":v?"border border-emerald-500/30 bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20":R?"border border-indigo-500/30 bg-indigo-500/10 text-indigo-400":"border border-gray-600 text-gray-400 hover:text-white hover:border-indigo-400 hover:bg-indigo-500/10"}`,onClick:()=>{G(null),I(null),document.getElementById("zip-plugin-uploader")?.click()},children:R?e.jsxs(e.Fragment,{children:[e.jsx(K,{size:11,className:"animate-spin"}),e.jsx("span",{children:s==="zh"?"分析中...":"Analyzing..."})]}):v?e.jsxs(e.Fragment,{children:[e.jsx(Ge,{size:11}),e.jsx("span",{className:"max-w-[100px] truncate",children:v.name})]}):D?e.jsxs(e.Fragment,{children:[e.jsx(st,{size:11}),e.jsx("span",{children:s==="zh"?"重试":"Retry"})]}):e.jsxs(e.Fragment,{children:[e.jsx(Ke,{size:11}),e.jsx("span",{children:s==="zh"?"从 ZIP 安装":"Install from ZIP"})]})}),e.jsxs("button",{onClick:()=>r(zs),className:"px-2.5 py-1 text-xs uppercase font-bold text-gray-400 hover:text-white bg-gray-800 hover:bg-gray-700 rounded-lg transition-all flex items-center gap-1 cursor-pointer",title:"Reset to default example",children:[e.jsx(Jt,{size:11,className:"text-indigo-450"}),s==="zh"?"示例:智能测验生成器":"Quiz Sample"]})]})]}),y&&!N&&e.jsx("div",{className:"px-4 pt-4 bg-gray-950",children:e.jsx(E,{})}),e.jsxs("div",{className:"flex-1 flex overflow-hidden min-h-0 bg-gray-950",children:[e.jsxs("div",{className:"w-7/12 flex flex-col border-r border-gray-800 h-full p-4 min-h-0",children:[e.jsxs("div",{className:"flex justify-between items-center mb-1 text-xs uppercase font-bold text-gray-400 select-none shrink-0",children:[e.jsx("span",{children:s==="zh"?"⚙️ 插件主程序 JS 源代码":"⚙️ Plugin Source Code (JavaScript)"}),e.jsx("span",{className:"font-mono text-xs text-gray-500",children:"Node Sandbox Ready"})]}),e.jsx("textarea",{value:t,onChange:m=>r(m.target.value),className:"w-full flex-1 font-mono text-xs p-4 bg-gray-900 border border-gray-800 text-indigo-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none resize-none leading-relaxed overflow-y-auto"})]}),e.jsxs("div",{className:"w-5/12 flex flex-col bg-gray-900/40 p-4 h-full overflow-y-auto min-h-0",children:[e.jsxs("div",{className:"mb-3",children:[e.jsxs("div",{className:"text-xs uppercase font-bold text-gray-400 select-none mb-1.5 flex justify-between items-center",children:[e.jsx("span",{children:s==="zh"?"🔍 MANIFEST 实时解析与权限审计":"🔍 Manifest Extraction & Audit"}),e.jsx("span",{className:"text-xs px-1.5 py-0.5 rounded bg-indigo-950 text-indigo-400 border border-indigo-900 font-mono",children:"Live Static"})]}),(()=>{const m=Ss(t),l=m&&m.manifest&&m.manifest.id&&m.manifest.name;return e.jsxs("div",{className:"space-y-3.5",children:[e.jsx("div",{className:`p-3 rounded-lg border flex items-start gap-2 ${l?"bg-emerald-950/45 border-emerald-800/60 text-emerald-300":"bg-amber-955/40 border-amber-800/60 text-amber-300"}`,children:l?e.jsxs(e.Fragment,{children:[e.jsx(Ge,{size:16,className:"text-emerald-400 shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h5",{className:"text-xs font-bold font-sans",children:s==="zh"?"✓ Manifest 静态合法性验证通过":"✓ Manifest Validation Passed"}),e.jsx("p",{className:"text-xs text-emerald-400/80 mt-0.5 leading-tight",children:s==="zh"?"检测到完整的插件标识。可在安全白名单和命令总线中顺利完成挂载。":"Completed identifier extraction. Secure initialization is ready to deploy."})]})]}):e.jsxs(e.Fragment,{children:[e.jsx(st,{size:16,className:"text-amber-400 shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h5",{className:"text-xs font-bold font-sans",children:s==="zh"?"⚠️ 未匹配到有效 Manifest 描述符":"⚠️ Searching for valid Metadata"}),e.jsx("p",{className:"text-xs text-amber-400/80 mt-0.5 leading-tight",children:s==="zh"?"请在代码段中指定完整的 manifest 包含 id、name 属性,系统才能自动进行预览与权限挂载。":"Please provide manifest object inside exports.default with unique id/name properties to active automatic registration."})]})]})}),l&&m&&m.manifest&&e.jsxs("div",{className:"bg-gray-900 border border-gray-800 rounded-xl p-3.5 space-y-2.5",children:[e.jsxs("div",{className:"border-b border-gray-800 pb-2 flex justify-between items-center",children:[e.jsx("h6",{className:"text-xs font-bold text-gray-300 uppercase tracking-wider",children:s==="zh"?"基本描述元数据":"Metadata Details"}),e.jsxs("span",{className:"text-xs text-indigo-400 font-mono px-1 bg-indigo-950 rounded",children:["v",m.manifest.version||"1.0.0"]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-2 text-xs font-mono",children:[e.jsx("div",{className:"text-gray-500",children:s==="zh"?"名称:":"Name:"}),e.jsx("div",{className:"col-span-2 text-gray-200 font-sans font-semibold",children:m.manifest.name}),e.jsx("div",{className:"text-gray-500",children:s==="zh"?"唯一标识:":"UUID/ID:"}),e.jsx("div",{className:"col-span-2 text-gray-305",children:m.manifest.id}),e.jsx("div",{className:"text-gray-500",children:s==="zh"?"开发者:":"Author:"}),e.jsx("div",{className:"col-span-2 text-indigo-305",children:m.manifest.author||"Community"})]})]})]})})()]}),e.jsxs("div",{className:"mt-5 bg-gray-900 border border-gray-800 rounded-xl p-3.5 space-y-3 shrink-0",children:[e.jsxs("h6",{className:"text-xs font-bold text-indigo-300 uppercase tracking-wider pb-1.5 border-b border-gray-800 flex items-center justify-between",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(It,{size:11,className:"text-indigo-400 animate-pulse"}),e.jsx("span",{children:s==="zh"?`已加载插件管理 (${i.length})`:`Active Plugins (${i.length})`})]}),e.jsx("button",{onClick:()=>O("store"),className:"text-xs text-gray-500 hover:text-indigo-400 transition-colors uppercase tracking-wider font-semibold",children:s==="zh"?"管理大图 ➔":"View Grid ➔"})]}),e.jsx("div",{className:"space-y-2 max-h-48 overflow-y-auto pr-1",children:i.length===0?e.jsx("div",{className:"text-xs text-gray-550 italic py-2 text-center",children:s==="zh"?"暂无安装的插件":"No plugins sideloaded."}):i.map(m=>e.jsxs("div",{className:"p-2.5 bg-gray-950 border border-gray-900 rounded-lg flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"text-xs font-bold text-gray-200 truncate",children:m.name}),e.jsx("span",{className:`w-1.5 h-1.5 rounded-full shrink-0 ${m.status==="active"?"bg-emerald-500 animate-pulse":"bg-slate-500"}`})]}),e.jsx("span",{className:"text-xs text-gray-500 font-mono block truncate select-all",children:m.id})]}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("button",{onClick:()=>n(m.id),className:`px-2.5 py-1 text-xs font-bold rounded transition-colors ${m.status==="active"?"bg-amber-950/60 border border-amber-900/50 text-amber-400 hover:bg-amber-900/80":"bg-emerald-950/60 border border-emerald-900/50 text-emerald-400 hover:bg-emerald-900/80"}`,children:m.status==="active"?s==="zh"?"禁用":"Disable":s==="zh"?"启用":"Enable"}),!m.id.startsWith("@openlearn/")&&e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>{T(m.id),b.current?.click()},className:"px-2 py-1 text-xs font-bold bg-sky-950/60 border border-sky-900/50 text-sky-300 hover:bg-sky-900/80 rounded transition-colors",children:s==="zh"?"更新":"Update"}),e.jsx("button",{onClick:()=>g(m.id),className:"px-2 py-1 text-xs font-bold bg-red-950/60 border border-red-900/50 text-red-400 hover:bg-red-900/80 rounded transition-colors",children:s==="zh"?"删除":"Delete"})]})]})]},m.id))})]})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-800 bg-gray-950 flex justify-between items-center shrink-0 select-none",children:[e.jsx("span",{className:"text-xs text-gray-500 font-mono",children:"Secure Sideload Mode • Sandbox Integrity Check"}),e.jsx("div",{className:"flex justify-end gap-3",children:e.jsx("button",{onClick:x,disabled:c||!t.trim(),className:"px-4 py-2 text-xs bg-indigo-600 font-bold hover:bg-indigo-700 text-white rounded-lg transition-all flex items-center gap-2 disabled:opacity-50 hover:shadow-lg active:scale-97 cursor-pointer",children:c?e.jsxs(e.Fragment,{children:[e.jsx(K,{size:13,className:"animate-spin"}),e.jsx("span",{children:s==="zh"?"集成挂载中...":"Registering..."})]}):e.jsxs(e.Fragment,{children:[e.jsx(Ge,{size:13}),e.jsx("span",{children:s==="zh"?"部署并安装到课堂内核":"Deploy & Install Plugin"})]})})})]})]})}function Is({lang:s}){const[t,r]=be.useState([]),[i,c]=be.useState(!1),[x,n]=be.useState(!0),[g,T]=be.useState(""),[b,O]=be.useState(""),[D,G]=be.useState(""),v=async()=>{c(!0);try{let N="/api/admin/logs?limit=300";b&&(N+=`&level=${b}`),D&&(N+=`&component=${D}`);const m=await(await fetch(N)).json();m.success&&r(m.logs||[])}catch(N){console.error("Error fetching logs:",N)}finally{c(!1)}};be.useEffect(()=>{v()},[b,D]),be.useEffect(()=>{if(!x)return;const N=setInterval(()=>{let E="/api/admin/logs?limit=300";b&&(E+=`&level=${b}`),D&&(E+=`&component=${D}`),fetch(E).then(m=>m.json()).then(m=>{m.success&&r(m.logs||[])}).catch(m=>console.error(m))},3e3);return()=>clearInterval(N)},[x,b,D]);const I=be.useMemo(()=>{const N=new Set;return t.forEach(E=>{E.component&&N.add(E.component)}),Array.from(N)},[t]),R=be.useMemo(()=>{if(!g)return t;const N=g.toLowerCase();return t.filter(E=>{const m=(E.msg||"").toLowerCase(),l=(E.component||"").toLowerCase();return m.includes(N)||l.includes(N)})},[t,g]),y=N=>{switch(N){case"error":return"text-rose-500 font-semibold";case"warn":return"text-amber-500 font-semibold";case"debug":return"text-blue-400";default:return"text-emerald-400"}};return e.jsxs("div",{className:"flex-1 flex flex-col p-6 bg-slate-900 text-slate-100 font-mono text-sm overflow-hidden h-full min-h-[500px]",children:[e.jsxs("div",{className:"flex flex-wrap gap-4 items-center justify-between border-b border-slate-800 pb-4 shrink-0 font-sans",children:[e.jsxs("div",{className:"flex flex-wrap gap-3 items-center",children:[e.jsx("input",{type:"text",placeholder:s==="zh"?"搜索日志内容...":"Search logs...",value:g,onChange:N=>T(N.target.value),className:"bg-slate-800 border border-slate-700 text-slate-200 px-3 py-1.5 rounded-lg focus:outline-none focus:border-indigo-500 placeholder-slate-500 text-xs w-48"}),e.jsxs("select",{value:b,onChange:N=>O(N.target.value),className:"bg-slate-800 border border-slate-700 text-slate-200 px-3 py-1.5 rounded-lg focus:outline-none focus:border-indigo-500 text-xs",children:[e.jsx("option",{value:"",children:s==="zh"?"所有级别":"All Levels"}),e.jsx("option",{value:"info",children:"INFO"}),e.jsx("option",{value:"warn",children:"WARN"}),e.jsx("option",{value:"error",children:"ERROR"}),e.jsx("option",{value:"debug",children:"DEBUG"})]}),e.jsxs("select",{value:D,onChange:N=>G(N.target.value),className:"bg-slate-800 border border-slate-700 text-slate-200 px-3 py-1.5 rounded-lg focus:outline-none focus:border-indigo-500 text-xs max-w-48",children:[e.jsx("option",{value:"",children:s==="zh"?"所有组件":"All Components"}),I.map(N=>e.jsx("option",{value:N,children:N},N))]})]}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("label",{className:"flex items-center gap-2 text-xs text-slate-400 select-none cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:x,onChange:N=>n(N.target.checked),className:"rounded bg-slate-800 border-slate-700 text-indigo-500 focus:ring-0 focus:ring-offset-0"}),s==="zh"?"自动刷新 (3s)":"Auto-refresh (3s)"]}),e.jsxs("button",{onClick:v,disabled:i,className:"bg-slate-800 hover:bg-slate-700 active:bg-slate-750 text-slate-200 border border-slate-700 px-3 py-1.5 rounded-lg text-xs flex items-center gap-1.5 transition-colors disabled:opacity-50 cursor-pointer",children:[i?e.jsx(K,{size:12,className:"animate-spin"}):null,s==="zh"?"刷新":"Refresh"]})]})]}),e.jsx("div",{className:"flex-1 overflow-y-auto mt-4 p-4 bg-slate-950 border border-slate-800 rounded-xl flex flex-col gap-1.5 min-h-0 select-text",children:R.length===0?e.jsx("div",{className:"text-center py-12 text-slate-500 text-xs",children:s==="zh"?"暂无匹配的运行日志数据":"No logs found."}):R.map((N,E)=>e.jsxs("div",{className:"flex gap-3 text-xs leading-relaxed hover:bg-slate-900/60 p-1 rounded transition-colors",children:[e.jsx("span",{className:"text-slate-500 shrink-0 select-none",children:new Date(N.timestamp||Date.now()).toLocaleTimeString()}),e.jsx("span",{className:`uppercase font-bold w-12 shrink-0 ${y(N.level)}`,children:N.level||"info"}),e.jsxs("span",{className:"text-indigo-400 shrink-0 font-semibold w-24 truncate",title:N.component,children:["[",N.component||"system","]"]}),e.jsxs("span",{className:"text-slate-200 flex-1 break-all whitespace-pre-wrap",children:[N.msg,N.meta&&Object.keys(N.meta).length>0&&e.jsx("span",{className:"text-slate-500 ml-2 font-mono text-xs",children:JSON.stringify(N.meta)})]})]},E))})]})}function $s(s){if(!s)return"-";try{return new Date(s).toLocaleString()}catch{return"-"}}function Us({lang:s,items:t,loading:r,onRefresh:i,onExport:c,onPurge:x,onDownloadArchive:n}){const g=s==="zh",[T,b]=o.useState(null),[O,D]=o.useState(null);o.useEffect(()=>{i()},[]);const G=async(v,I)=>{D(v);try{await I()}finally{D(null)}};return e.jsxs("div",{className:"flex-1 overflow-y-auto bg-gray-50/30 p-6",children:[e.jsxs("div",{className:"mb-4 flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsxs("h3",{className:"flex items-center gap-2 text-sm font-bold text-gray-900",children:[e.jsx(Xe,{size:16,className:"text-indigo-600"}),g?"插件数据(卸载后保留)":"Plugin Data (kept after uninstall)"]}),e.jsx("p",{className:"mt-1 max-w-2xl text-xs text-gray-500",children:g?"卸载插件默认保留其数据表与配置,重新安装同一插件会自动接回。清除前服务端会先导出 SQL 归档,此操作不可恢复。":"Uninstalling keeps plugin tables and config by default; reinstalling the same plugin re-attaches them. Purging exports a SQL archive first and cannot be undone."})]}),e.jsxs("button",{type:"button",onClick:()=>void i(),className:"flex shrink-0 items-center gap-1.5 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-600 shadow-sm transition-colors hover:border-indigo-300 hover:text-indigo-600",children:[e.jsx(Fe,{size:13,className:r?"animate-spin":""}),g?"刷新":"Refresh"]})]}),t.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center rounded-2xl border border-dashed border-gray-200 bg-white/60 py-16 text-center",children:[e.jsx(Ct,{size:28,className:"mb-3 text-gray-300"}),e.jsx("p",{className:"text-sm font-medium text-gray-500",children:g?"暂无保留的插件数据":"No retained plugin data"}),e.jsx("p",{className:"mt-1 text-xs text-gray-400",children:g?"卸载插件时若选择「保留数据」,会在这里出现。":'Data appears here when a plugin is uninstalled with "keep data".'})]}):e.jsx("div",{className:"space-y-3",children:t.map(v=>{const I=`${v.manifestId}:${v.pluginId}`,R=O===v.pluginId,y=T===v.pluginId;return e.jsxs("div",{className:"rounded-2xl border border-gray-200 bg-white p-4 shadow-sm",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"truncate text-sm font-bold text-gray-900",children:v.pluginName||v.manifestId}),v.pluginVersion&&e.jsxs("span",{className:"rounded bg-gray-100 px-1.5 py-0.5 text-[10px] font-semibold text-gray-500",children:["v",v.pluginVersion]})]}),e.jsx("div",{className:"mt-0.5 truncate font-mono text-[11px] text-gray-400",children:v.manifestId}),e.jsxs("div",{className:"mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-gray-500",children:[e.jsxs("span",{children:[g?"数据表":"Tables",": ",e.jsx("strong",{className:"text-gray-700",children:v.tableCount})]}),e.jsxs("span",{children:[g?"数据行":"Rows",": ",e.jsx("strong",{className:"text-gray-700",children:v.rowCount})]}),e.jsxs("span",{children:[g?"配置键":"Storage keys",": ",e.jsx("strong",{className:"text-gray-700",children:v.storageKeys})]}),e.jsxs("span",{children:[g?"迁移版本":"Migration",":"," ",e.jsx("strong",{className:"text-gray-700",children:v.migrationVersion??"-"})]}),e.jsxs("span",{children:[g?"卸载时间":"Uninstalled",":"," ",e.jsx("strong",{className:"text-gray-700",children:$s(v.uninstalledAt)})]})]}),v.lastExportPath&&e.jsxs("div",{className:"mt-1 truncate font-mono text-[11px] text-emerald-600",children:[g?"最近归档":"Latest archive",": ",v.lastExportPath]})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[e.jsxs("button",{type:"button",disabled:R,onClick:()=>void G(v.pluginId,()=>c(v)),className:"flex items-center gap-1 rounded-lg bg-sky-50 px-3 py-1.5 text-xs font-medium text-sky-700 transition-colors hover:bg-sky-100 disabled:opacity-50",children:[R?e.jsx(K,{size:12,className:"animate-spin"}):e.jsx(Qe,{size:12}),g?"导出归档":"Export"]}),n&&v.lastExportPath&&e.jsxs("button",{type:"button",onClick:()=>n(v),className:"flex items-center gap-1 rounded-lg bg-gray-50 px-3 py-1.5 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-100",children:[e.jsx(Qe,{size:12}),g?"下载":"Download"]}),e.jsxs("button",{type:"button",disabled:R,onClick:()=>b(y?null:v.pluginId),className:"flex items-center gap-1 rounded-lg bg-red-50 px-3 py-1.5 text-xs font-medium text-red-600 transition-colors hover:bg-red-100 disabled:opacity-50",children:[e.jsx(Be,{size:12}),g?"清除数据":"Purge"]})]})]}),y&&e.jsxs("div",{className:"mt-3 flex flex-wrap items-center justify-between gap-3 rounded-xl border border-red-200 bg-red-50 px-3 py-2",children:[e.jsxs("div",{className:"flex items-start gap-2 text-xs text-red-700",children:[e.jsx(Pe,{size:14,className:"mt-0.5 shrink-0"}),e.jsx("span",{children:g?`将清除 ${v.tableCount} 张表、${v.rowCount} 行数据与 ${v.storageKeys} 个配置键。服务端会先导出 SQL 归档,但清除后无法直接恢复。`:`This clears ${v.tableCount} tables, ${v.rowCount} rows and ${v.storageKeys} storage keys. A SQL archive is exported first, but the data cannot be restored in place.`})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",onClick:()=>b(null),className:"rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-medium text-gray-600 hover:bg-gray-50",children:g?"取消":"Cancel"}),e.jsxs("button",{type:"button",disabled:R,onClick:()=>void G(v.pluginId,async()=>{await x(v),b(null)}),className:"flex items-center gap-1 rounded-lg bg-red-600 px-3 py-1.5 text-xs font-semibold text-white transition-colors hover:bg-red-700 disabled:bg-red-300",children:[R?e.jsx(K,{size:12,className:"animate-spin"}):e.jsx(Be,{size:12}),g?"确认清除":"Confirm purge"]})]})]})]},I)})})]})}function Ps({pluginName:s,manifestId:t,lang:r,onCancel:i,onConfirm:c}){const[x,n]=o.useState(!1),[g,T]=o.useState(!1),b=r==="zh",O=()=>{if(x){if(!g)return;c("purge");return}c("keep")};return e.jsx("div",{className:"fixed inset-0 z-[120] flex items-center justify-center bg-black/40 p-4",children:e.jsxs("div",{className:"w-full max-w-lg rounded-2xl bg-white shadow-2xl",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-gray-100 px-6 py-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Be,{size:18,className:"text-red-500"}),e.jsx("h3",{className:"text-base font-bold text-gray-900",children:b?`卸载「${s}」`:`Uninstall "${s}"`})]}),e.jsx("button",{type:"button",onClick:i,className:"rounded-lg p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-700","aria-label":b?"关闭":"Close",children:e.jsx(Se,{size:18})})]}),e.jsxs("div",{className:"space-y-3 px-6 py-5",children:[e.jsx("p",{className:"text-sm text-gray-600",children:b?"卸载后插件功能立即停用。请选择如何处理该插件保存的数据(学生作业、考勤、成绩等)。":"The plugin stops working immediately. Choose what happens to the data it stored (submissions, attendance, scores…)."}),e.jsxs("label",{className:`flex cursor-pointer items-start gap-3 rounded-xl border p-4 transition-colors ${x?"border-gray-200 bg-gray-50/40":"border-indigo-300 bg-indigo-50/40"}`,children:[e.jsx("input",{type:"radio",name:"uninstall-data-policy",checked:!x,onChange:()=>n(!1),className:"mt-1"}),e.jsxs("span",{className:"space-y-1",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-sm font-semibold text-gray-900",children:[e.jsx(Xe,{size:14,className:"text-indigo-600"}),b?"保留插件数据(推荐)":"Keep plugin data (recommended)"]}),e.jsx("span",{className:"block text-xs text-gray-500",children:b?"插件表、迁移版本与配置原样留库。重新安装同一插件时会自动接回旧数据,也可以在「插件数据」页随时导出或清除。":"Tables, migration version and config stay in the database. Reinstalling the same plugin re-attaches them; you can export or clear them later under Plugin Data."})]})]}),e.jsxs("label",{className:`flex cursor-pointer items-start gap-3 rounded-xl border p-4 transition-colors ${x?"border-red-300 bg-red-50/50":"border-gray-200 bg-gray-50/40"}`,children:[e.jsx("input",{type:"radio",name:"uninstall-data-policy",checked:x,onChange:()=>n(!0),className:"mt-1"}),e.jsxs("span",{className:"space-y-1",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-sm font-semibold text-red-700",children:[e.jsx(Pe,{size:14}),b?"卸载并清除插件数据(不可恢复)":"Uninstall and purge plugin data (irreversible)"]}),e.jsx("span",{className:"block text-xs text-gray-500",children:b?"服务端会先把插件表导出为 SQL 归档(保存在数据目录 plugin-data-exports/),确认归档成功后再删除数据,并写入审计日志。":"The server exports the plugin tables to a SQL archive (data dir plugin-data-exports/) before deleting anything, then writes an audit record."})]})]}),x&&e.jsxs("label",{className:"flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700",children:[e.jsx("input",{type:"checkbox",checked:g,onChange:D=>T(D.target.checked),className:"mt-0.5"}),e.jsx("span",{children:b?`我已知晓:清除「${t}」的数据不可恢复(请以归档文件为准)。`:`I understand that purging data for "${t}" is irreversible (rely on the archive).`})]})]}),e.jsxs("div",{className:"flex items-center justify-end gap-3 border-t border-gray-100 px-6 py-4",children:[e.jsx("button",{type:"button",onClick:i,className:"rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-50",children:b?"取消":"Cancel"}),e.jsx("button",{type:"button",onClick:O,disabled:x&&!g,className:`rounded-lg px-4 py-2 text-sm font-semibold text-white transition-colors ${x?g?"bg-red-600 hover:bg-red-700":"cursor-not-allowed bg-red-300":"bg-indigo-600 hover:bg-indigo-700"}`,children:x?b?"卸载并清除数据":"Uninstall & purge":b?"卸载并保留数据":"Uninstall & keep data"})]})]})})}function Es(s){return!Number.isFinite(s)||s<=0?"0 B":s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/(1024*1024)).toFixed(2)} MB`}function Rs({plugin:s,lang:t,onClose:r,onRolledBack:i,fetcher:c}){const x=t==="zh",n=o.useId(),g=o.useRef(null),T=o.useMemo(()=>c??((p,f)=>fetch(p,f)),[c]),[b,O]=o.useState([]),[D,G]=o.useState(!0),[v,I]=o.useState(null),[R,y]=o.useState(null),[N,E]=o.useState(null),[m,l]=o.useState(null);o.useEffect(()=>{const p=g.current;if(p)return p.open||(typeof p.showModal=="function"?p.showModal():p.setAttribute("open","")),()=>{p.open&&typeof p.close=="function"?p.close():p.removeAttribute("open")}},[]);const j=o.useCallback(async()=>{G(!0),I(null);try{const p=await T(`/api/plugins/${encodeURIComponent(s.id)}/snapshots`),f=await p.json().catch(()=>null);if(!p.ok||!f?.success)throw new Error(f?.error||(x?"加载快照失败":"Failed to load snapshots"));O(Array.isArray(f.snapshots)?f.snapshots:[])}catch(p){I(p instanceof Error?p.message:String(p))}finally{G(!1)}},[T,s.id,x]);o.useEffect(()=>{j()},[j]);const k=async p=>{E(p),I(null);try{const f=await T(`/api/plugins/${encodeURIComponent(s.id)}/rollback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({snapshotId:p})}),F=await f.json().catch(()=>null);if(!f.ok||!F?.success)throw new Error(F?.error||(x?"回滚失败":"Rollback failed"));y(null),i?.({restoredVersion:F.restoredVersion||"",fromVersion:F.fromVersion||s.version||""})}catch(f){I(f instanceof Error?f.message:String(f))}finally{E(null)}},M=async p=>{l(p),I(null);try{const f=await T(`/api/plugins/${encodeURIComponent(s.id)}/snapshots/${encodeURIComponent(p)}`,{method:"DELETE"}),F=await f.json().catch(()=>null);if(!f.ok||!F?.success)throw new Error(F?.error||(x?"删除快照失败":"Failed to delete snapshot"));R===p&&y(null),O(Q=>Q.filter(de=>de.snapshotId!==p))}catch(f){I(f instanceof Error?f.message:String(f))}finally{l(null)}};return e.jsxs("dialog",{ref:g,"aria-labelledby":n,onCancel:p=>{p.preventDefault(),N||r()},className:"m-auto w-full max-w-xl rounded-2xl bg-white p-0 shadow-2xl border border-gray-200 text-gray-900 [overscroll-behavior:contain] [&::backdrop]:bg-black/45 [&::backdrop]:overflow-hidden [&::backdrop]:[overscroll-behavior:contain]",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-gray-100 px-6 py-4",children:[e.jsxs("div",{className:"flex items-center gap-2.5 min-w-0",children:[e.jsx(zt,{size:18,className:"text-indigo-600 shrink-0"}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("h3",{id:n,className:"text-base font-bold text-gray-900 truncate",children:x?`「${s.name}」历史快照与安全回滚`:`"${s.name}" Snapshots & Safe Rollback`}),e.jsxs("p",{className:"text-xs text-gray-500 font-mono truncate",children:[s.manifestId||s.id," · ",x?"当前版本":"Current"," v",s.version||"0.0.0"]})]})]}),e.jsx("button",{type:"button",onClick:()=>{N||r()},"aria-label":x?"关闭对话框":"Close dialog",className:"text-gray-400 hover:text-gray-700 p-1.5 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer",children:e.jsx(Se,{size:18})})]}),e.jsxs("div",{className:"p-6 flex flex-col gap-4 max-h-[70vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex items-start gap-2.5 rounded-xl border border-indigo-100 bg-indigo-50/60 px-3.5 py-2.5 text-xs text-indigo-900",children:[e.jsx(St,{size:16,className:"text-indigo-600 shrink-0 mt-0.5"}),e.jsx("span",{children:x?"每次插件更新前都会自动保存 1 份更新前完整快照(包含插件文件、私有数据库表、存储键值、配置与权限)。若新版本存在缺陷或升级异常,可随时安全回滚。":"Every update automatically captures 1 full pre-update snapshot (plugin files, private DB tables, storage, config, and grants) so you can safely roll back if needed."})]}),v&&e.jsxs("div",{className:"flex items-start gap-2 rounded-xl border border-red-200 bg-red-50 px-3.5 py-2.5 text-xs text-red-700",children:[e.jsx(Pe,{size:15,className:"shrink-0 mt-0.5"}),e.jsx("span",{children:v})]}),D?e.jsxs("div",{className:"flex items-center justify-center gap-2 py-10 text-sm text-gray-500",children:[e.jsx(K,{size:16,className:"animate-spin text-indigo-600"}),e.jsx("span",{children:x?"正在读取更新快照…":"Loading snapshots…"})]}):b.length===0?e.jsx("div",{className:"rounded-xl border border-dashed border-gray-200 bg-gray-50/70 px-4 py-8 text-center text-xs text-gray-500",children:x?"暂无可用的更新前快照。执行插件更新后,系统将自动在此保留最新 1 份更新前快照。":"No pre-update snapshots yet. The latest pre-update snapshot is automatically created before any plugin update."}):e.jsx("div",{className:"flex flex-col gap-3",children:b.map(p=>{const f=R===p.snapshotId,F=N===p.snapshotId,Q=m===p.snapshotId;return e.jsxs("div",{className:"rounded-xl border border-gray-200 bg-white p-4 shadow-sm flex flex-col gap-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"flex flex-col gap-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsxs("span",{className:"text-xs font-mono font-bold px-2 py-0.5 rounded bg-indigo-50 text-indigo-700 border border-indigo-200",children:["v",p.fromVersion]}),p.toVersion&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-xs text-gray-400",children:"➔"}),e.jsxs("span",{className:"text-xs font-mono text-gray-600 bg-gray-100 px-2 py-0.5 rounded border border-gray-200",children:["v",p.toVersion]})]}),e.jsx("span",{className:"text-[11px] text-gray-400 font-mono",children:Es(p.sizeBytes)})]}),e.jsxs("span",{className:"text-xs text-gray-500",children:[x?"快照生成于:":"Captured: ",new Date(p.createdAt).toLocaleString()]})]}),e.jsx("button",{type:"button",onClick:()=>void M(p.snapshotId),disabled:!!N||Q,title:x?"删除快照":"Delete snapshot","aria-label":x?"删除快照":"Delete snapshot",className:"p-1.5 rounded-lg text-gray-400 hover:text-red-600 hover:bg-red-50 transition-colors cursor-pointer disabled:opacity-40",children:Q?e.jsx(K,{size:14,className:"animate-spin"}):e.jsx(Be,{size:14})})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3 text-xs text-gray-600 bg-slate-50 border border-slate-200/80 rounded-lg px-3 py-2",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(Xe,{size:12,className:"text-indigo-500"}),x?`${p.tableCount} 张私有表 / ${p.rowCount} 行`:`${p.tableCount} tables / ${p.rowCount} rows`]}),e.jsx("span",{children:x?`存储键 ${p.storageRows} 项`:`${p.storageRows} storage keys`}),e.jsx("span",{children:x?`配置 ${p.configRows} 项`:`${p.configRows} configs`}),p.migrationVersion!==null&&e.jsx("span",{className:"font-mono",children:x?`迁移 v${p.migrationVersion}`:`Migration v${p.migrationVersion}`})]}),e.jsx("div",{className:"flex items-center justify-end gap-2 min-h-[34px]",children:f?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-xs text-amber-700 mr-auto",children:x?`确认将插件代码与数据恢复至 v${p.fromVersion}?`:`Restore files and DB to v${p.fromVersion}?`}),e.jsx("button",{type:"button",onClick:()=>y(null),disabled:F,autoFocus:!0,className:"px-3 py-1.5 text-xs font-medium rounded-lg border border-gray-200 text-gray-600 hover:bg-gray-50 cursor-pointer",children:x?"取消":"Cancel"}),e.jsxs("button",{type:"button",onClick:()=>void k(p.snapshotId),disabled:F,className:"px-3 py-1.5 text-xs font-semibold rounded-lg bg-amber-600 text-white hover:bg-amber-700 flex items-center gap-1.5 cursor-pointer disabled:opacity-50",children:[F?e.jsx(K,{size:12,className:"animate-spin"}):e.jsx(gt,{size:12}),e.jsx("span",{children:x?"确认回滚":"Confirm Rollback"})]})]}):e.jsxs("button",{type:"button",onClick:()=>y(p.snapshotId),disabled:!!N||Q,className:"px-3 py-1.5 text-xs font-semibold rounded-lg border border-amber-200 bg-amber-50 text-amber-800 hover:bg-amber-100 flex items-center gap-1.5 transition-colors cursor-pointer disabled:opacity-50",children:[e.jsx(gt,{size:12}),e.jsx("span",{children:x?`回滚至 v${p.fromVersion}`:`Rollback to v${p.fromVersion}`})]})})]},p.snapshotId)})})]}),e.jsx("div",{className:"flex items-center justify-end border-t border-gray-100 px-6 py-3 bg-gray-50/60",children:e.jsx("button",{type:"button",onClick:()=>{N||r()},className:"px-4 py-1.5 text-xs font-medium rounded-lg text-gray-600 hover:bg-gray-200/70 transition-colors cursor-pointer",children:x?"关闭":"Close"})})]})}function Ts({plugins:s,lang:t,isAdmin:r,storeTab:i,setStoreTab:c,pluginCode:x,setPluginCode:n,installingPlugin:g,onInstall:T,onZipUpload:b,onToggle:O,onDelete:D,orphanData:G=[],orphanDataLoading:v=!1,fetchOrphanData:I,handleExportOrphanData:R,handlePurgeOrphanData:y,handleDownloadOrphanArchive:N}){const[E,m]=o.useState(!1),[l,j]=o.useState(!1),[k,M]=o.useState(null),[p,f]=o.useState(null),F=o.useRef(null),[Q,de]=o.useState(null),Ce=S=>{const z=s.find(P=>P.id===S);let B=S;try{B=JSON.parse(z?.manifest??"{}")?.id??S}catch{B=S}de({id:S,name:z?.name??B,manifestId:B})},[U,ee]=o.useState(null),[ce,fe]=o.useState(null),[se,Ee]=o.useState(!1),[ue,ve]=o.useState(null),[ae,te]=o.useState(null),[he,ye]=o.useState(new Map),[xe,ne]=o.useState([]),[Ie,Ne]=o.useState(null),[$e,Re]=o.useState(null),[W,H]=o.useState(null),[pe,Z]=o.useState(null);o.useEffect(()=>{let S=!0;return fetch("/api/plugins/market").then(z=>z.json()).then(z=>{if(z?.success&&Array.isArray(z.market)&&S){const B=new Map;for(const P of z.market)B.set(P.manifestId,P);ye(B)}}).catch(()=>{}),fetch("/api/plugins/community").then(z=>z.json()).then(z=>{z?.success&&Array.isArray(z.plugins)&&S&&ne(z.plugins)}).catch(()=>{}),()=>{S=!1}},[]);const{mergedMarketMap:we,communityUpdateCount:ge,storeUpdateCount:Y}=o.useMemo(()=>{const S=new Map(he);let z=0;for(const P of xe){if(!P.hasUpdate)continue;z+=1;const L=S.get(P.id);!L||!L.hasUpdate?S.set(P.id,{manifestId:P.id,hasUpdate:!0,latestVersion:P.version,downloadUrl:P.downloadUrl||null,releaseUrl:P.homepage||P.repository||null,changelog:P.changelog||P.description||null,repository:P.repository||P.homepage||null,integrity:P.integrity||void 0,signature:P.signature||void 0,source:"community"}):!L.downloadUrl&&P.downloadUrl&&S.set(P.id,{...L,downloadUrl:P.downloadUrl,integrity:L.integrity??P.integrity??void 0,signature:L.signature??P.signature??void 0})}let B=0;for(const P of s){let L=P.id;try{const q=JSON.parse(P.manifest);q?.id&&(L=q.id)}catch{}S.get(L)?.hasUpdate&&(B+=1)}return{mergedMarketMap:S,communityUpdateCount:z,storeUpdateCount:B}},[xe,he,s]),Le=()=>{Z(t==="zh"?"🎉 社区插件已安装,正在刷新插件列表…":"🎉 Community plugin installed, reloading the plugin list…"),setTimeout(()=>window.location.reload(),1200)},Te=async(S,z)=>{Re(S);try{const B=await fetch(`/api/plugins/${encodeURIComponent(S)}/check-update`,{method:"POST"}).then(P=>P.json());B?.success&&ye(P=>{const L=new Map(P);return L.set(z,{...B,manifestId:z}),L})}catch{}finally{Re(null)}},Ae=async(S,z)=>{if(!z?.downloadUrl){const B=s.find(P=>P.id===S);H(B?{...B,marketItem:z}:{id:S,name:S,marketItem:z});return}Ne(S),Z(t==="zh"?"正在创建更新前快照并执行一键热更新...":"Saving pre-update snapshot and hot-updating...");try{const B=JSON.stringify({downloadUrl:z.downloadUrl,...z.integrity?{integrity:z.integrity}:{},...z.signature?{signature:z.signature}:{}}),P=await fetch(`/api/plugins/${encodeURIComponent(S)}/one-click-update`,{method:"POST",headers:{"Content-Type":"application/json"},body:B}).then(L=>L.json());if(P?.success)Z(t==="zh"?`🎉 插件已热更新至 v${P.newVersion||z.latestVersion||"最新版本"}(已保存更新前快照)!`:`🎉 Hot updated to v${P.newVersion||z.latestVersion||"latest"} (snapshot saved)!`),setTimeout(()=>window.location.reload(),1200);else if(P?.fallbackToClient&&z?.downloadUrl){Z(t==="zh"?"服务端下载超时,切换至浏览器直传(仍会生成快照)...":"Server download timed out, switching to browser transfer (with snapshot)...");try{const L=await fetch(z.downloadUrl);if(!L.ok)throw new Error(`HTTP ${L.status}`);const q=await L.arrayBuffer(),Ue=await fetch(`/api/plugins/${encodeURIComponent(S)}/update-zip-raw`,{method:"POST",headers:{"Content-Type":"application/octet-stream","x-install-mode":"update","x-community-install":"true",...z.integrity?{"x-integrity":z.integrity}:{},...z.signature?{"x-signature":z.signature}:{}},body:q}).then(Me=>Me.json());Ue?.success?(Z(t==="zh"?`🎉 插件已热更新至 v${Ue.newVersion||z.latestVersion||"最新版本"}(已保存更新前快照)!`:"🎉 Hot updated (snapshot saved)!"),setTimeout(()=>window.location.reload(),1200)):(Z(`❌ 更新失败: ${Ue?.error||"客户端上传失败"}`),setTimeout(()=>Z(null),4e3))}catch(L){Z(`❌ 客户端下载失败: ${L.message}`),setTimeout(()=>Z(null),4e3)}}else Z(`❌ 更新失败: ${P?.error||"受热更新包限制"}`),setTimeout(()=>Z(null),4e3)}catch(B){Z(`❌ 一键热更新失败: ${B.message}`),setTimeout(()=>Z(null),4e3)}finally{Ne(null)}},Oe=S=>{const z=S.target.files?.[0];z&&M(z)},oe=s.some(S=>S.execution_mode==="legacy");function ie(){return e.jsxs("div",{className:"bg-amber-50 border border-amber-200 rounded-lg p-4 flex items-start justify-between mb-4",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(Pe,{size:20,className:"text-amber-500 shrink-0 mt-0.5"}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-bold text-amber-800",children:t==="zh"?"发现可迁移的旧格式插件":"Legacy Plugin Detected"}),e.jsx("p",{className:"text-xs text-amber-700 mt-1",children:t==="zh"?"该插件使用旧格式运行。上传新格式 ZIP 包以完成迁移,迁移后旧版本可安全卸载。":"This plugin runs in legacy mode. Upload a new-format ZIP package to migrate. The old version can be safely uninstalled afterwards."})]})]}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsx("button",{onClick:()=>document.getElementById("zip-plugin-uploader")?.click(),className:"bg-amber-600 text-white hover:bg-amber-700 rounded-lg text-sm font-medium px-4 py-2 transition-colors cursor-pointer",children:t==="zh"?"迁移到新格式":"Migrate to New Format"}),e.jsx("button",{onClick:()=>j(!0),className:"text-amber-500 hover:text-amber-700 p-1 cursor-pointer",children:e.jsx(Se,{size:16})})]})]})}return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex-1 flex flex-col h-full overflow-y-auto",children:e.jsxs("div",{className:"bg-white border text-gray-900 border-gray-200 rounded-2xl shadow flex flex-col overflow-hidden h-full",children:[e.jsxs("div",{className:"px-6 py-4 flex items-center justify-between border-b border-gray-100 bg-gray-50/50 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-6",children:[e.jsxs("h2",{className:"font-bold text-gray-900 flex items-center gap-2 text-lg",children:[e.jsx(It,{size:20,className:"text-indigo-600"}),t==="zh"?"Openlearn Next 插件中心":"Openlearn Next App Store"]}),e.jsxs("div",{className:"flex bg-gray-200/50 p-1 rounded-lg",children:[e.jsxs("button",{onClick:()=>c("store"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1.5 cursor-pointer ${i==="store"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx("span",{children:t==="zh"?"发现":"Discover"}),Y>0&&e.jsx("span",{className:"px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-indigo-600 text-white leading-none",children:Y})]}),e.jsxs("button",{onClick:()=>c("community"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1.5 cursor-pointer ${i==="community"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx(ot,{size:14})," ",e.jsx("span",{children:t==="zh"?"社区":"Community"}),ge>0&&e.jsx("span",{"data-testid":"community-update-badge",className:"px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-amber-500 text-white leading-none",children:ge})]}),e.jsxs("button",{onClick:()=>c("dev"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1 cursor-pointer ${i==="dev"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx(Zt,{size:14})," ",t==="zh"?"开发者":"Developer"]}),e.jsxs("button",{onClick:()=>c("logs"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1 cursor-pointer ${i==="logs"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx(qt,{size:14})," ",t==="zh"?"系统日志":"Logs"]}),e.jsxs("button",{onClick:()=>c("data"),className:`px-4 py-1.5 text-sm font-medium rounded-md transition-colors flex items-center gap-1 cursor-pointer ${i==="data"?"bg-white shadow text-indigo-600 font-bold":"text-gray-600 hover:text-gray-900"}`,children:[e.jsx(Xe,{size:14})," ",t==="zh"?"插件数据":"Data"]})]})]}),i==="store"&&e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("label",{className:"flex items-center gap-2 cursor-pointer text-xs font-semibold text-gray-500 hover:text-gray-900 select-none transition-colors border border-gray-200 rounded-lg px-3 py-1.5 bg-gray-50/50 shadow-sm",children:[e.jsx("input",{type:"checkbox",checked:E,onChange:S=>m(S.target.checked),className:"w-3.5 h-3.5 rounded text-indigo-600 border-gray-300 focus:ring-indigo-500 cursor-pointer"}),e.jsx("span",{children:t==="zh"?"显示系统核心插件":"Show System Core Plugins"})]}),e.jsx("button",{type:"button",className:`flex items-center gap-1.5 cursor-pointer text-xs font-semibold select-none transition-colors border rounded-lg px-3 py-1.5 shadow-sm ${k?"border-emerald-300 bg-emerald-50 text-emerald-700":"border-teal-200 bg-teal-50/50 text-teal-700 hover:border-teal-400 hover:bg-teal-50"}`,onClick:()=>{document.getElementById("zip-plugin-uploader")?.click()},children:k?e.jsxs(e.Fragment,{children:[e.jsx(Ge,{size:14,className:"text-emerald-500"}),e.jsx("span",{className:"max-w-[120px] truncate",children:k.name})]}):e.jsxs(e.Fragment,{children:[e.jsx(Ke,{size:14}),e.jsx("span",{children:t==="zh"?"从 ZIP 安装":"Install from ZIP"})]})})]})]}),e.jsx("input",{type:"file",accept:".zip",id:"zip-plugin-uploader",className:"hidden",onChange:S=>{f(null),Oe(S)}}),e.jsx("input",{ref:F,type:"file",accept:".zip",className:"hidden",onChange:S=>{const z=S.target.files?.[0];z&&M(z),S.target.value=""}}),i==="store"?e.jsx(cs,{plugins:s,lang:t,isAdmin:r,showSystemPlugins:E,marketMap:we,checkingUpdateId:$e,oneClickUpdatingId:Ie,handleCheckUpdate:Te,handleOneClickUpdate:Ae,setChangelogModalPlugin:H,setSettingsPlugin:ve,onOpenSnapshots:te,setUpdateTargetPluginId:f,updateFileInputRef:F,onToggle:O,onDelete:Ce}):i==="community"?e.jsx(ks,{installedPlugins:s,lang:t,canConfigure:r,onInstalled:Le,onRegistryLoaded:ne}):i==="logs"?e.jsx(Is,{lang:t}):i==="data"?e.jsx(Us,{lang:t,items:G,loading:v,onRefresh:()=>void I?.(),onExport:S=>void R?.(S),onPurge:S=>void y?.(S),onDownloadArchive:N}):e.jsx(Cs,{lang:t,pluginCode:x,setPluginCode:n,plugins:s,installingPlugin:g,onInstall:T,onToggle:O,onDelete:Ce,setUpdateTargetPluginId:f,updateFileInputRef:F,setStoreTab:c,zipError:U,setZipError:ee,zipPreview:ce,setZipPreview:fe,zipProcessing:se,hasLegacyPlugins:oe,dismissMigration:l,MigrationPromptBanner:ie})]})}),ue&&e.jsx(Xt,{pluginId:ue.id,pluginName:ue.name,manifestStr:ue.manifest,lang:t,onClose:()=>ve(null)}),e.jsx(ls,{isOpen:!!k,onClose:()=>{M(null),f(null)},lang:t,file:k,lockedTargetPluginId:p,installedPlugins:s,onConfirmInstall:b}),W&&e.jsx("div",{className:"fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white rounded-2xl max-w-lg w-full p-6 shadow-2xl border border-gray-100 flex flex-col gap-4",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-gray-100 pb-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(xt,{className:"text-indigo-600",size:20}),e.jsxs("h3",{className:"text-base font-bold text-gray-900",children:[W.name," ",t==="zh"?"版本更新说明":"Release Notes"]})]}),e.jsx("button",{onClick:()=>H(null),className:"text-gray-400 hover:text-gray-600 p-1 rounded-lg hover:bg-gray-100 transition-colors",children:e.jsx(Se,{size:18})})]}),e.jsxs("div",{className:"flex items-center justify-between bg-indigo-50/70 border border-indigo-100 rounded-xl p-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs font-mono font-bold bg-white text-gray-600 px-2 py-0.5 rounded border border-gray-200",children:["v",W.version||"1.1.0"]}),e.jsx("span",{className:"text-indigo-400 font-bold",children:"➔"}),e.jsxs("span",{className:"text-xs font-mono font-bold bg-indigo-600 text-white px-2 py-0.5 rounded shadow-sm",children:["v",W.marketItem?.latestVersion||"1.2.0"]})]}),ze(W.marketItem?.repository)&&e.jsxs("a",{href:ze(W.marketItem?.repository),target:"_blank",rel:"noopener noreferrer",className:"flex items-center gap-1 text-xs font-mono text-indigo-600 hover:text-indigo-800 font-medium",children:[e.jsx(mt,{size:12}),e.jsx("span",{children:"Git Repo"}),e.jsx(it,{size:10})]})]}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx("h4",{className:"text-xs font-bold text-gray-700 uppercase tracking-wider",children:t==="zh"?"新特性与优化变更清单 (Changelog)":"What's New"}),e.jsx("div",{className:"bg-slate-50 border border-slate-200/80 rounded-xl p-3.5 text-xs text-slate-700 leading-relaxed font-mono whitespace-pre-wrap max-h-48 overflow-y-auto",children:W.marketItem?.changelog||(t==="zh"?`1. 阶段式任务逻辑与自动化测试增强
|
|
66
|
+
2. 前端轻量化与高可用平滑升级`:"1. General enhancements and bug fixes")})]}),e.jsxs("div",{className:"flex items-center justify-end gap-3 pt-2 border-t border-gray-100",children:[e.jsx("button",{onClick:()=>H(null),className:"px-4 py-2 text-xs font-medium rounded-lg text-gray-600 hover:bg-gray-100 transition-colors cursor-pointer",children:t==="zh"?"稍后再说":"Later"}),W.marketItem?.downloadUrl?e.jsx("button",{onClick:()=>{const S=W.id,z=W.marketItem;H(null),Ae(S,z)},className:"px-4 py-2 text-xs font-bold rounded-lg bg-gradient-to-r from-blue-600 to-indigo-600 text-white hover:from-blue-700 hover:to-indigo-700 shadow-md transition-all flex items-center gap-1.5 cursor-pointer",children:e.jsxs("span",{children:["🚀 ",t==="zh"?"立即一键热更新":"Update Now"]})}):e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[ze(W.marketItem?.releaseUrl||W.marketItem?.repository)&&e.jsxs("a",{href:ze(W.marketItem?.releaseUrl||W.marketItem?.repository),target:"_blank",rel:"noopener noreferrer",className:"px-3 py-2 text-xs font-semibold rounded-lg border border-indigo-200 bg-indigo-50 text-indigo-700 hover:bg-indigo-100 flex items-center gap-1",children:[e.jsx("span",{children:t==="zh"?"前往 Release 页面下载":"Download from Release"}),e.jsx(it,{size:12})]}),e.jsxs("button",{type:"button",onClick:()=>{const S=W.id;H(null),f(S),F.current?.click()},className:"px-3 py-2 text-xs font-semibold rounded-lg bg-amber-600 text-white hover:bg-amber-700 cursor-pointer flex items-center gap-1",children:[e.jsx(Ke,{size:12}),e.jsx("span",{children:t==="zh"?"上传 ZIP 更新":"Upload ZIP"})]})]})]})]})}),pe&&e.jsxs("div",{className:"fixed bottom-6 right-6 z-50 bg-gray-900/90 backdrop-blur text-white text-xs font-medium px-4 py-3 rounded-xl shadow-2xl border border-gray-700/80 flex items-center gap-2",children:[e.jsx(Fe,{size:14,className:"text-indigo-400 animate-spin"}),e.jsx("span",{children:pe})]}),Q&&e.jsx(Ps,{pluginName:Q.name,manifestId:Q.manifestId,lang:t,onCancel:()=>de(null),onConfirm:S=>{const z=Q;de(null),D(z.id,S)}}),ae&&e.jsx(Rs,{plugin:ae,lang:t,onClose:()=>te(null),onRolledBack:S=>{te(null),Z(t==="zh"?`已安全回滚至 v${S.restoredVersion},正在刷新…`:`Rolled back safely to v${S.restoredVersion}, reloading…`),setTimeout(()=>window.location.reload(),1e3)}})]})}function Vs(s={}){const t=Yt(),r=s.plugins??t?.plugins??[],i=s.lang??t?.lang??"zh",c=s.storeTab??t?.storeTab??"store",x=s.setStoreTab??t?.setStoreTab??(()=>{}),n=s.pluginCode??t?.pluginCode??"",g=s.setPluginCode??t?.setPluginCode??(()=>{}),T=s.installingPlugin??t?.installingPlugin??!1,b=s.onInstall??t?.handleInstallPlugin??(()=>{}),O=s.onZipUpload??t?.handleZipPluginUpload??(async()=>{}),D=s.onToggle??t?.handleTogglePlugin??(async()=>{}),G=s.onDelete??t?.handleDeletePlugin??(async()=>{}),v=t?.session,I=v?v.role==="administrator"||v.subRole==="administrator":void 0;return e.jsx(Ts,{plugins:r,lang:i,isAdmin:I,storeTab:c,setStoreTab:R=>x(R),pluginCode:n,setPluginCode:g,installingPlugin:T,onInstall:()=>{b(n)},onZipUpload:O,onToggle:D,onDelete:G,orphanData:t?.orphanData,orphanDataLoading:t?.orphanDataLoading,fetchOrphanData:t?.fetchOrphanData,handleExportOrphanData:t?.handleExportOrphanData,handlePurgeOrphanData:t?.handlePurgeOrphanData,handleDownloadOrphanArchive:t?.handleDownloadOrphanArchive})}export{Vs as PluginView};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e}from"./vendor-react-lfWn1oGs.js";import{k as t}from"./index-CLtQGl25.js";import{aF as a}from"./vendor-icons-__JLt-CP.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";function f({showProcessLogs:o,setShowProcessLogs:r,processLogsContent:s,t:l}){return o?e.jsxs(t,{open:!!o,onClose:()=>r(null),ariaLabelledBy:"process-logs-dialog-title",closedBy:"any",className:"bg-white rounded-xl shadow-xl max-w-2xl max-h-[90vh]",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 flex items-center justify-between",children:[e.jsxs("h2",{id:"process-logs-dialog-title",className:"font-semibold text-gray-800 flex items-center gap-2",children:[e.jsx(a,{size:18,className:"text-gray-600"}),l.processLogsTitle||"Process Logs"]}),e.jsx("button",{type:"button",onClick:()=>r(null),"aria-label":"Close process logs",className:"text-gray-400 hover:text-gray-600 font-bold px-2 cursor-pointer",children:"×"})]}),e.jsx("div",{className:"p-4 flex-1 overflow-auto bg-gray-900 m-4 rounded flex flex-col",children:e.jsx("pre",{className:"font-mono text-xs text-green-400 whitespace-pre-wrap",children:s||"No logs generated."})})]}):null}export{f as ProcessLogsModal};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e}from"./vendor-react-lfWn1oGs.js";import{k as O}from"./index-CLtQGl25.js";import{aE as w,f as C,k}from"./vendor-icons-__JLt-CP.js";import"./vendor-utils-CsoI4M9x.js";import"./vendor-pptx-ByQkJ0Xi.js";import"./vendor-pdf-DNexfcCO.js";import"./vendor-charts-CK-CU_m7.js";import"./vendor-motion-DtyD8eRQ.js";function _(z){const{isQuizGeneratorOpen:S,setIsQuizGeneratorOpen:l,lessons:x,quizGenMode:n,setQuizGenMode:u,quizGenSelectedLessonId:d,setQuizGenSelectedLessonId:Q,quizGenTopic:c,setQuizGenTopic:M,isGeneratingSuggestions:p,setIsGeneratingSuggestions:h,suggestedObjectives:b,setSuggestedObjectives:j,suggestedQuestions:r,setSuggestedQuestions:m,quizGenTimeLimit:f,setQuizGenTimeLimit:G,savingQuiz:y,setSavingQuiz:v,quizGeneratorClassId:o,fetchClassDashboard:N}=z;return e.jsxs(O,{open:S,onClose:()=>l(!1),ariaLabelledBy:"quiz-generator-dialog-title",closedBy:"closerequest",className:"bg-white border text-gray-900 border-gray-200 rounded-2xl shadow-2xl max-w-3xl overflow-hidden max-h-[90vh] text-gray-850",children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 flex items-center justify-between bg-gray-50/80 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(w,{className:"text-indigo-600 animate-pulse",size:20}),e.jsx("h2",{id:"quiz-generator-dialog-title",className:"font-bold text-gray-800 text-lg",children:"AI-Objective Quiz Generator"})]}),e.jsx("button",{type:"button",onClick:()=>l(!1),"aria-label":"Close quiz generator",className:"text-gray-400 hover:text-gray-600 font-bold p-1 hover:bg-gray-200 rounded transition-colors cursor-pointer",children:"×"})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-6",children:[r.length===0&&e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"bg-indigo-50/80 p-4 rounded-xl border border-indigo-100 text-xs text-indigo-800 leading-relaxed font-sans",children:"Choose a lesson to scan. Our advanced AI model will run a deep semantic scan across the entire lesson curriculum content, discover your core learning objectives, and construct interactive multiple-choice questions aligning precisely with each of them."}),e.jsxs("div",{className:"font-sans",children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2",children:"Scan Core Selection Mode"}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("button",{onClick:()=>u("scan_lesson"),className:`p-3 rounded-lg border text-left flex flex-col transition-all ${n==="scan_lesson"?"border-indigo-600 bg-indigo-50/40 ring-2 ring-indigo-100":"border-gray-200 hover:border-gray-300"}`,children:[e.jsx("span",{className:"font-semibold text-sm text-indigo-900",children:"Curriculum Lesson Scanning"}),e.jsx("span",{className:"text-xs text-gray-500 mt-1",children:"Examines real Markdown content inside virtual lesson modules."})]}),e.jsxs("button",{onClick:()=>u("topic"),className:`p-3 rounded-lg border text-left flex flex-col transition-all ${n==="topic"?"border-indigo-600 bg-indigo-50/40 ring-2 ring-indigo-100":"border-gray-200 hover:border-gray-300"}`,children:[e.jsx("span",{className:"font-semibold text-sm text-indigo-900",children:"Custom Keyword / Topic"}),e.jsx("span",{className:"text-xs text-gray-500 mt-1",children:"Provide a custom prompt keyword or objective manually."})]})]})]}),n==="scan_lesson"?e.jsxs("div",{className:"font-sans",children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2",children:"Select Lesson to Scan"}),x.length===0?e.jsx("div",{className:"text-gray-500 text-xs py-3 border rounded border-dashed text-center",children:"No lessons available. Please create a lesson first."}):e.jsxs("select",{value:d,onChange:s=>Q(s.target.value),className:"w-full p-2.5 bg-white border border-gray-200 rounded-lg text-sm text-gray-800 focus:border-indigo-500 focus:outline-none shadow-sm",children:[e.jsx("option",{value:"",children:"-- Choose a lesson --"}),x.map(s=>e.jsx("option",{value:s.id,children:s.title},s.id))]})]}):e.jsxs("div",{className:"font-sans",children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-505 uppercase tracking-wider mb-2",children:"Custom Topic Prompt"}),e.jsx("input",{type:"text",placeholder:"e.g. Introduction to React state, Cloud SQL setup...",value:c,onChange:s=>M(s.target.value),className:"w-full p-2.5 bg-white border border-gray-200 rounded-lg text-sm focus:border-indigo-500 focus:outline-none shadow-sm text-gray-800"})]}),e.jsx("div",{className:"pt-4 flex justify-end font-sans",children:e.jsx("button",{disabled:p||n==="scan_lesson"&&!d||n==="topic"&&!c.trim(),onClick:async()=>{h(!0);try{if(n==="scan_lesson"){const s=await fetch(`/api/classes/${o}/assignments/suggest`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({lessonId:d})});if(s.ok){const t=await s.json();j(t.learningObjectives||[]),m((t.questions||[]).map(i=>({...i,selected:!0})))}else alert("Error generating suggestions. Please make sure the selected lesson has content.")}else(await fetch(`/api/classes/${o}/assignments/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({topic:c})})).ok?(await N(o),l(!1)):alert("Error generating topic quiz.")}catch(s){console.error(s),alert(s.message)}finally{h(!1)}},className:"px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 text-white rounded-lg font-medium text-xs shadow transition flex items-center gap-2",children:p?e.jsxs(e.Fragment,{children:[e.jsx(C,{size:16,className:"animate-spin"}),e.jsx("span",{children:"AI Scanning Content..."})]}):e.jsxs(e.Fragment,{children:[e.jsx(w,{size:16}),e.jsx("span",{children:"Generate Key MCQ Quiz"})]})})})]}),r.length>0&&e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"font-sans",children:[e.jsx("div",{className:"text-xs font-bold text-gray-500 uppercase tracking-wider mb-2",children:"Identified Core Objectives"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:b.map((s,t)=>e.jsxs("span",{className:"px-2.5 py-1 bg-teal-50 text-teal-800 border border-teal-200 rounded-full text-xs font-semibold",children:["🎯 ",s]},t))})]}),e.jsxs("div",{className:"space-y-4 font-sans",children:[e.jsx("div",{className:"text-xs font-bold text-gray-500 uppercase tracking-wider font-semibold",children:"Suggested MCQ Questions"}),e.jsx("div",{className:"space-y-3",children:r.map((s,t)=>e.jsxs("div",{className:"p-4 rounded-xl border border-gray-150 bg-gray-50/50 hover:bg-gray-50 transition space-y-3 text-gray-800 text-gray-850",children:[e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"w-5 h-5 rounded-full bg-indigo-150 text-indigo-700 flex items-center justify-center text-xs font-bold shrink-0",children:t+1}),e.jsxs("span",{className:"text-xs font-semibold text-teal-700 bg-teal-50 px-2.5 py-0.5 rounded border border-teal-100",children:["objective: ",s.objective]})]}),e.jsx("input",{type:"checkbox",checked:!!s.selected,onChange:i=>{const a=[...r];a[t].selected=i.target.checked,m(a)},className:"w-4 h-4 text-indigo-600 border-gray-300 rounded focus:ring-indigo-500"})]}),e.jsx("p",{className:"font-semibold text-gray-800 text-sm leading-relaxed",children:s.question}),e.jsx("div",{className:"grid grid-cols-2 gap-2 text-xs",children:s.options.map((i,a)=>{const g=i===s.correctAnswer;return e.jsxs("div",{className:`p-2 rounded border flex items-center justify-between ${g?"bg-green-50 border-green-200 text-green-955 font-semibold":"bg-white border-gray-100 text-gray-700"}`,children:[e.jsx("span",{children:i}),g&&e.jsx(k,{size:12,className:"text-green-600 shrink-0"})]},a)})})]},t))})]}),e.jsxs("div",{className:"pt-4 border-t border-gray-100 flex justify-between items-center font-sans",children:[e.jsx("button",{onClick:()=>{j([]),m([])},className:"px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg text-xs font-semibold transition",children:"Back / Scan New"}),e.jsxs("div",{className:"flex items-center gap-2 border border-indigo-150 bg-indigo-50/50 px-3 py-1.5 rounded-lg text-xs font-semibold text-indigo-900",children:[e.jsx("span",{children:"⏱️ Quiz Time Limit:"}),e.jsxs("select",{value:f,onChange:s=>G(Number(s.target.value)),className:"bg-transparent text-xs text-indigo-950 font-bold focus:outline-none cursor-pointer",children:[e.jsx("option",{className:"text-gray-800",value:0,children:"No Limit"}),e.jsx("option",{className:"text-gray-800",value:1,children:"1 Min"}),e.jsx("option",{className:"text-gray-800",value:2,children:"2 Mins"}),e.jsx("option",{className:"text-gray-800",value:5,children:"5 Mins"}),e.jsx("option",{className:"text-gray-800",value:10,children:"10 Mins"}),e.jsx("option",{className:"text-gray-800",value:15,children:"15 Mins"}),e.jsx("option",{className:"text-gray-800",value:20,children:"20 Mins"}),e.jsx("option",{className:"text-gray-800",value:30,children:"30 Mins"}),e.jsx("option",{className:"text-gray-800",value:45,children:"45 Mins"}),e.jsx("option",{className:"text-gray-800",value:60,children:"60 Mins"})]})]}),e.jsxs("button",{disabled:y||r.filter(s=>s.selected).length===0,onClick:async()=>{v(!0);try{const s=x.find(a=>a.id===d),t=s?s.title:c||"Custom Objective";(await fetch(`/api/classes/${o}/assignments/create-suggested-quiz`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:`MCQ Evaluation: ${t}`,description:"Automatic evaluation based on core learning objectives.",questions:r.filter(a=>a.selected).map(({selected:a,...g})=>g),learningObjectives:b,timeLimit:f})})).ok?(await N(o),l(!1)):alert("Failed to save suggested quiz.")}catch(s){console.error(s),alert(s.message)}finally{v(!1)}},className:"px-5 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-medium text-xs shadow hover:shadow-md transition flex items-center gap-2",children:[y?e.jsx(C,{size:16,className:"animate-spin"}):e.jsx(k,{size:16}),e.jsx("span",{children:"Create Assessment Quiz"})]})]})]})]})]})}export{_ as QuizGeneratorModal};
|