openlearn-next 0.7.9 → 0.7.10
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 +4 -2
- package/cli-data.mjs +1 -1
- package/dist/assets/{ActivityWorkspaceWidget-DbspLRxB.js → ActivityWorkspaceWidget-BU88GGUG.js} +1 -1
- package/dist/assets/{AdminDirectoryView-C2ZFIwHx.js → AdminDirectoryView-Cdsk9Mdr.js} +1 -1
- package/dist/assets/{AssessmentCenter-DbJAcTvr.js → AssessmentCenter-BoSWbJMt.js} +1 -1
- package/dist/assets/{AssignmentFilePreview-DEVTb4YJ.js → AssignmentFilePreview-D_POvLmv.js} +1 -1
- package/dist/assets/{BatchPickerModal-czw6eA6e.js → BatchPickerModal-DiHd_6Tg.js} +1 -1
- package/dist/assets/{ClassesView-B1F0tTtB.js → ClassesView-CWKqzWH0.js} +1 -1
- package/dist/assets/{ClassroomCountdownWidget-C9pv3GLy.js → ClassroomCountdownWidget-B5-KUVU8.js} +1 -1
- package/dist/assets/{CloudDriveModal-goDB6dlC.js → CloudDriveModal-CjcvdDiw.js} +1 -1
- package/dist/assets/{ComputerLabView-Csqiqvw2.js → ComputerLabView-CNTcmWAh.js} +1 -1
- package/dist/assets/{CourseManagement-BD-4z2OZ.js → CourseManagement-ha_iSnLU.js} +1 -1
- package/dist/assets/{CourseWizardModal-NXwhuph_.js → CourseWizardModal-oVT9mYSG.js} +1 -1
- package/dist/assets/{Dashboard-CFDWNYSw.js → Dashboard-Vb9coh9_.js} +1 -1
- package/dist/assets/{ExportWeightModal-dBxYVUP4.js → ExportWeightModal-BEesz6rd.js} +1 -1
- package/dist/assets/{HelpView-DSZC-1F0.js → HelpView--Um9DFYx.js} +1 -1
- package/dist/assets/{ImportLessonsModal-m_s44eus.js → ImportLessonsModal-DXGopj4j.js} +1 -1
- package/dist/assets/{InteractiveCoursewareViewer-C7faFOFD.js → InteractiveCoursewareViewer-DzgF9_LU.js} +1 -1
- package/dist/assets/{InteractiveWhiteboard-10fno-bJ.js → InteractiveWhiteboard-DJqgP_tU.js} +7 -7
- package/dist/assets/{LazyCourseware-DNn0O8lA.js → LazyCourseware-6yb_QiYS.js} +2 -2
- package/dist/assets/{LazyWhiteboard-BxpydfnE.js → LazyWhiteboard-CsF9GB6J.js} +2 -2
- package/dist/assets/{LessonEditorView-CMCgnrJD.js → LessonEditorView-CZTZ7Q0B.js} +1 -1
- package/dist/assets/{LiveClassroomView-BA0us3bY.js → LiveClassroomView-D2ugupT9.js} +1 -1
- package/dist/assets/{NotificationDetailModal-BMrt09PY.js → NotificationDetailModal-mUeIQP4R.js} +1 -1
- package/dist/assets/{PluginView-B6gCF1ZB.js → PluginView-CT3-zS2n.js} +1 -1
- package/dist/assets/{QuizGeneratorModal-BNW6tZ78.js → QuizGeneratorModal-ViKxNX5Q.js} +1 -1
- package/dist/assets/{StudentAssignmentEvalPanel-DMwBSOXf.js → StudentAssignmentEvalPanel-BRxHMABY.js} +1 -1
- package/dist/assets/{StudentAssignmentView-cQpPme4_.js → StudentAssignmentView-g-ewJIS3.js} +1 -1
- package/dist/assets/{StudentLessonView-CpGgYV-b.js → StudentLessonView-Dbvr3Xlo.js} +1 -1
- package/dist/assets/{StudentView-BfXi6-NQ.js → StudentView-20vfEk4v.js} +2 -2
- package/dist/assets/{SystemResourceLibraryModal-d4vfqM_9.js → SystemResourceLibraryModal-CDKeaKf8.js} +1 -1
- package/dist/assets/{TeacherView-DSJCvFE-.js → TeacherView-KIKuhXGF.js} +2 -2
- package/dist/assets/{TimetableView-D9Rhnvfj.js → TimetableView-7V2AwWml.js} +1 -1
- package/dist/assets/{index-DCnGJbif.css → index-C4cHQZyx.css} +1 -1
- package/dist/assets/{index-DuxQtTr9.js → index-XduPa2-x.js} +31 -31
- package/dist/assets/{pluginParsers-HRw6CVog.js → pluginParsers-BO6U1E-h.js} +1 -1
- package/dist/assets/{student-workspace-DO1mSl_I.js → student-workspace-DstiW0pM.js} +1 -1
- package/dist/env-validation.cjs +14 -6
- package/dist/index.html +2 -2
- package/dist/server.cjs +681 -346
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a5 as _,a6 as l}from"./index-
|
|
1
|
+
import{a5 as _,a6 as l}from"./index-XduPa2-x.js";const L=s=>{const a=[];try{const i=s.match(/manifest\s*:\s*\{([\s\S]*?)\}(?:\s*,|\s*\})/),c=i?i[1]:s,m=c.match(/id\s*:\s*['"]([^'"]+)['"]/)||s.match(/id\s*:\s*['"]([^'"]+)['"]/),p=c.match(/name\s*:\s*['"]([^'"]+)['"]/)||s.match(/name\s*:\s*['"]([^'"]+)['"]/),o=c.match(/version\s*:\s*['"]([^'"]+)['"]/)||s.match(/version\s*:\s*['"]([^'"]+)['"]/),v=c.match(/description\s*:\s*['"]([^'"]+)['"]/)||s.match(/description\s*:\s*['"]([^'"]+)['"]/),h=c.match(/author\s*:\s*['"]([^'"]+)['"]/)||s.match(/author\s*:\s*['"]([^'"]+)['"]/);let r=[];const u=c.match(/capabilitiesProposed\s*:\s*\[([\s\S]*?)\]/)||s.match(/capabilitiesProposed\s*:\s*\[([\s\S]*?)\]/);u&&(r=u[1].split(",").map(t=>t.replace(/['"\s]/g,"")).filter(t=>t.length>0));const w={id:m?.[1]||void 0,name:p?.[1]||void 0,version:o?.[1]||void 0,description:v?.[1]||void 0,author:h?.[1]||void 0,capabilitiesProposed:r.length>0?r:void 0},x=/actionRegistry\.register\s*\(\s*\{([\s\S]*?)\}\s*\)/g;let f;const g=s;for(;(f=x.exec(g))!==null;){const t=f[1],n=t.match(/id\s*:\s*['"]([^'"]+)['"]/),e=t.match(/commandType\s*:\s*['"]([^'"]+)['"]/),d=t.match(/description\s*:\s*['"]([^'"]+)['"]/);(n||e)&&a.push({id:n?n[1]:"unknown",commandType:e?e[1]:"unknown",description:d?d[1]:""})}return{manifest:w,actions:a,error:null}}catch(i){return{manifest:null,actions:[],error:i.toString()}}},y=s=>{const a=s.split(/\r?\n/);if(a.length<2)return[];const i=a[0],c=[",",";"," "];let m=",",p=0;c.forEach(t=>{const n=i.split(t).length;n>p&&(p=n,m=t)});const o=_(i,m).map(t=>t.toLowerCase().replace(/["'\r]/g,"").trim()),v=l(o,["学生学号","student_number","studentnumber","student number","student_id","studentid","学号","编号"]),h=l(o,["学生姓名","student_name","studentname","student name","姓名","学生","name"]),r=l(o,["学生邮箱","student_email","studentemail","student email","邮箱","email","mail"]),u=l(o,["初始密码","initial password","password","passwd","pwd","密码"]),w=l(o,["学生备注","student notes","private_notes","备注","note","notes","remark"]),x=h>=0?h:0,f=r>=0?r:1,g=[];for(let t=1;t<a.length;t++){if(!a[t].trim())continue;const n=_(a[t],m),e=b=>b>=0&&n[b]?n[b].replace(/["'\r]/g,"").trim():"",d=e(x);if(!d)continue;const I=e(v),S=e(u),M=e(w);g.push({name:d,email:e(f),...I?{student_number:I}:{},...S?{password:S}:{},...M?{private_notes:M}:{}})}return g};export{y as a,L as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as d,j as r,R as x}from"./vendor-react-DHoBZpn2.js";import{E as I,i as M}from"./index-DuxQtTr9.js";import{L as A}from"./LazyWhiteboard-BxpydfnE.js";import{S as W}from"./StudentAssignmentEvalPanel-DMwBSOXf.js";import{aq as C,h as z}from"./vendor-icons-DLOPlM4i.js";import{ActivityWorkspaceWidget as P}from"./ActivityWorkspaceWidget-DbspLRxB.js";import"./vendor-utils-BxOuk3id.js";import"./vendor-pdf-DVWGghIL.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-skggaP9D.js";import"./AssignmentFilePreview-DEVTb4YJ.js";class w{constructor(){this.providers=new Map}register(e){if(!e||!e.id)throw new Error("WorkspaceSlotRegistry Error: WorkspaceSlotProvider must have a valid ID.");this.providers.set(e.id,e)}unregister(e){return this.providers.delete(e)}getProviders(e){const t=Array.from(this.providers.values()).filter(s=>s.slot===e);return t.sort((s,i)=>(i.priority??0)-(s.priority??0)),Object.freeze(t)}listAll(){return Object.freeze(Array.from(this.providers.values()))}clear(){this.providers.clear()}}const k=d.createContext(null),N=({registry:n,children:e})=>{const[t]=d.useState(()=>n??new w),[,s]=d.useState(0),i=d.useCallback(()=>{s(l=>l+1)},[]),a=d.useCallback(l=>{t.register(l),i()},[t,i]),c=d.useCallback(l=>{t.unregister(l)&&i()},[t,i]),o=d.useCallback(l=>t.getProviders(l),[t]),g=d.useMemo(()=>({registry:t,registerSlotProvider:a,unregisterSlotProvider:c,getSlotProviders:o}),[t,a,c,o]);return r.jsx(k.Provider,{value:g,children:e})},_=()=>{const n=d.useContext(k);if(!n)throw new Error("useWorkspace must be used within a WorkspaceProvider");return n},D=n=>{const{getSlotProviders:e}=_();return e(n)},f=({slot:n,className:e})=>{const t=D(n);return t.length===0?null:r.jsx("div",{className:`workspace-slot workspace-slot-${n.toLowerCase()} ${e??""}`,children:t.map(s=>r.jsx(x.Fragment,{children:s.render()},s.id))})},L=({className:n,children:e})=>r.jsxs("div",{className:`workspace-shell flex flex-col h-screen w-screen overflow-hidden bg-slate-900 text-slate-100 ${n??""}`,children:[r.jsx(f,{slot:"TopBar",className:"w-full flex-none z-30"}),r.jsxs("div",{className:"flex flex-1 overflow-hidden relative",children:[r.jsx(f,{slot:"LeftSidebar",className:"flex-none z-20"}),r.jsxs("main",{className:"flex-1 relative overflow-hidden flex flex-col",children:[r.jsx(f,{slot:"MainCanvas",className:"flex-1 w-full h-full relative"}),e,r.jsx(f,{slot:"BottomPanel",className:"flex-none z-20"})]}),r.jsx(f,{slot:"RightSidebar",className:"flex-none z-20"})]}),r.jsx(f,{slot:"StatusBar",className:"w-full flex-none z-30"}),r.jsx(f,{slot:"FloatingArea",className:"absolute inset-0 pointer-events-none z-40"}),r.jsx(f,{slot:"DialogArea",className:"absolute inset-0 pointer-events-none z-50"})]});var b=(n=>(n[n.Immediate=0]="Immediate",n[n.High=1]="High",n[n.Normal=2]="Normal",n[n.Low=3]="Low",n[n.Idle=4]="Idle",n))(b||{});class E{constructor(){this.subscribers=new Map}subscribe(e,t){const s=String(e);this.subscribers.has(s)||this.subscribers.set(s,new Set);const i=this.subscribers.get(s);return i.add(t),()=>{i.delete(t)}}async publish(e,t,s="runtime.system"){const i={id:`evt_${globalThis.crypto.randomUUID()}`,type:e,payload:t,source:s,timestamp:Date.now()},a=this.subscribers.get(String(e))||new Set,c=this.subscribers.get("*")||new Set,o=[...Array.from(a),...Array.from(c)];return await Promise.all(o.map(g=>Promise.resolve(g(i)).catch(l=>{console.error(`[RuntimeEventBus] Error in subscriber for ${String(e)}:`,l)}))),i}clear(){this.subscribers.clear()}}class O{constructor(e=`rt_${globalThis.crypto.randomUUID()}`){this.listeners=new Set,this.currentState={runtime:{id:e,lifecycle:"Create",startTime:Date.now(),elapsedTime:0},lesson:{},stage:{index:0},activity:{},whiteboard:{objectCount:0,isLocked:!1},teachingObjects:[],students:[],plugin:[],ai:{isGenerating:!1},analytics:{totalInteractions:0,activeStudentCount:0,averageScore:0}}}getState(){return this.currentState}setState(e){this.currentState=Object.freeze(e),this.notify()}updateState(e){this.currentState=Object.freeze({...this.currentState,...e,runtime:e.runtime?{...this.currentState.runtime,...e.runtime}:this.currentState.runtime,lesson:e.lesson?{...this.currentState.lesson,...e.lesson}:this.currentState.lesson,stage:e.stage?{...this.currentState.stage,...e.stage}:this.currentState.stage,activity:e.activity?{...this.currentState.activity,...e.activity}:this.currentState.activity,whiteboard:e.whiteboard?{...this.currentState.whiteboard,...e.whiteboard}:this.currentState.whiteboard,ai:e.ai?{...this.currentState.ai,...e.ai}:this.currentState.ai,analytics:e.analytics?{...this.currentState.analytics,...e.analytics}:this.currentState.analytics}),this.notify()}setLifecycle(e){this.updateState({runtime:{...this.currentState.runtime,lifecycle:e}})}addStudent(e){this.currentState.students.some(s=>s.id===e.id)||this.updateState({students:Object.freeze([...this.currentState.students,e])})}removeStudent(e){this.updateState({students:Object.freeze(this.currentState.students.filter(t=>t.id!==e))})}subscribe(e){return this.listeners.add(e),e(this.getState()),()=>{this.listeners.delete(e)}}notify(){const e=this.getState();for(const t of this.listeners)try{t(e)}catch(s){console.error("[RuntimeStateManager] Listener error:",s)}}}class ${constructor(){this.services=new Map}registerService(e){if(!e.serviceId)throw new Error("[RuntimeServiceRegistry] Service must provide a non-empty serviceId.");this.services.set(e.serviceId,e)}async unregisterService(e){const t=this.services.get(e);return t?(await t.dispose(),this.services.delete(e)):!1}getService(e){return this.services.get(e)}listServices(){return Object.freeze(Array.from(this.services.keys()))}async initializeAll(e){for(const t of this.services.values())await t.initialize(e)}async disposeAll(){for(const e of this.services.values())try{await e.dispose()}catch(t){console.error(`[RuntimeServiceRegistry] Error disposing service ${e.serviceId}:`,t)}this.services.clear()}}class B{constructor(){this.modules=new Map,this.activeModuleIds=new Set}registerModule(e){if(!e.id)throw new Error("[RuntimeModuleRegistry] Module must specify a valid id.");this.modules.set(e.id,e)}async unregisterModule(e,t){const s=this.modules.get(e);return s?(this.activeModuleIds.has(e)&&t&&(await s.stop(t),this.activeModuleIds.delete(e)),await s.dispose(),this.modules.delete(e)):!1}getModule(e){return this.modules.get(e)}listModules(){return Object.freeze(Array.from(this.modules.values()))}async startAll(e){for(const t of this.modules.values())await t.initialize(e),await t.start(e),this.activeModuleIds.add(t.id)}async stopAll(e){for(const t of this.activeModuleIds){const s=this.modules.get(t);if(s)try{await s.stop(e)}catch(i){console.error(`[RuntimeModuleRegistry] Error stopping module ${t}:`,i)}}this.activeModuleIds.clear()}}class U{constructor(){this.taskQueue=[],this.isProcessing=!1,this.timer=null}schedule(e,t,s=b.Normal,i=0,a=3){return new Promise((c,o)=>{const g={id:`tsk_${globalThis.crypto.randomUUID()}`,name:e,priority:s,taskFn:async()=>{i>0&&await new Promise(l=>setTimeout(l,i));try{const l=await t();return c(l),l}catch(l){throw o(l),l}},delayMs:i,maxRetries:a,createdAt:Date.now()};this.taskQueue.push(g),this.sortQueue(),queueMicrotask(()=>{this.processQueue()})})}start(){this.timer||(this.timer=setInterval(()=>{this.processQueue()},50))}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}getPendingTaskCount(){return this.taskQueue.length}sortQueue(){this.taskQueue.sort((e,t)=>e.priority-t.priority||e.createdAt-t.createdAt)}async processQueue(){if(this.isProcessing||this.taskQueue.length===0)return;this.isProcessing=!0;const e=this.taskQueue.shift();if(e){let t=0;const s=e.maxRetries??1;for(;t<s;)try{await e.taskFn();break}catch(i){t+=1,t>=s&&console.error(`[RuntimeScheduler] Task ${e.name} (${e.id}) failed after ${t} attempts:`,i)}}this.isProcessing=!1}}class T{constructor(){this.rolePermissions=new Map([["Teacher",new Set(["lesson:control","stage:navigate","whiteboard:draw","quiz:submit","plugin:execute","ai:invoke","session:manage"])],["Assistant",new Set(["stage:navigate","whiteboard:draw","quiz:submit","plugin:execute","ai:invoke"])],["Student",new Set(["whiteboard:draw","quiz:submit","ai:invoke","plugin:execute"])],["Observer",new Set([])],["Plugin",new Set(["whiteboard:draw","plugin:execute","ai:invoke"])],["AI",new Set(["stage:navigate","whiteboard:draw","ai:invoke"])]])}hasPermission(e,t){const s=this.rolePermissions.get(e);return s?s.has(t):!1}grantPermission(e,t){this.rolePermissions.has(e)||this.rolePermissions.set(e,new Set),this.rolePermissions.get(e).add(t)}revokePermission(e,t){const s=this.rolePermissions.get(e);s&&s.delete(t)}getRolePermissions(e){const t=this.rolePermissions.get(e);return Object.freeze(t?Array.from(t):[])}}class F{constructor(){this.resources=new Map}registerResource(e,t,s){const i=Array.from(this.resources.values()).find(c=>c.url===e);if(i)return i;const a={id:`res_${globalThis.crypto.randomUUID()}`,type:t,url:e,sizeBytes:s,status:"loaded",cachedAt:Date.now()};return this.resources.set(a.id,a),a}getResource(e){return this.resources.get(e)}listResources(e){const t=Array.from(this.resources.values());return Object.freeze(e?t.filter(s=>s.type===e):t)}removeResource(e){return this.resources.delete(e)}clear(){this.resources.clear()}}class H{constructor(){this.snapshots=new Map}takeSnapshot(e,t,s,i){const a={snapshotId:`snap_${globalThis.crypto.randomUUID()}`,timestamp:Date.now(),stateTree:JSON.parse(JSON.stringify(e)),activeServices:[...t],loadedModules:[...s],resources:[...i]};return this.snapshots.set(a.snapshotId,a),this.lastAutoSnapshot=a,a}getLatestSnapshot(){return this.lastAutoSnapshot}getSnapshot(e){return this.snapshots.get(e)}clear(){this.snapshots.clear(),this.lastAutoSnapshot=void 0}}class Q{constructor(){this.hooks=new Map}registerHook(e,t){this.hooks.has(e)||this.hooks.set(e,new Set);const s=this.hooks.get(e);return s.add(t),()=>{s.delete(t)}}async executeHook(e,t,s){const i=this.hooks.get(e);if(!(!i||i.size===0))for(const a of i)try{await a(t,s)}catch(c){console.error(`[RuntimeHooksManager] Error executing hook "${e}":`,c)}}clear(){this.hooks.clear()}}class q{constructor(e,t){this.activeSession=null,this.eventBus=e,this.stateManager=t}async createSession(e,t,s,i){this.activeSession&&this.activeSession.status==="active"&&await this.destroySession();const a={sessionId:e,courseId:s,lessonId:i,teacher:t,students:Object.freeze([]),createdAt:Date.now(),status:"active"};return this.activeSession=a,this.stateManager.updateState({lesson:{activeLessonId:i,status:"ready"}}),await this.eventBus.publish("SessionCreated",{sessionId:e,teacherId:t.id,timestamp:Date.now()}),a}async joinSession(e){if(!this.activeSession||this.activeSession.status!=="active")return!1;const t=[...this.activeSession.students.filter(s=>s.id!==e.id),{...e,isOnline:!0}];return this.activeSession={...this.activeSession,students:Object.freeze(t)},this.stateManager.addStudent(e),await this.eventBus.publish("StudentJoined",{student:e,timestamp:Date.now()}),!0}async leaveSession(e){if(!this.activeSession||this.activeSession.status!=="active")return!1;const t=this.activeSession.students.map(s=>s.id===e?{...s,isOnline:!1}:s);return this.activeSession={...this.activeSession,students:Object.freeze(t)},this.stateManager.removeStudent(e),await this.eventBus.publish("StudentLeft",{studentId:e,timestamp:Date.now()}),!0}async reconnectSession(e){if(!this.activeSession||this.activeSession.status!=="active")return!1;const t=this.activeSession.students.find(i=>i.id===e);if(!t)return!1;const s={...t,isOnline:!0};return this.joinSession(s)}async destroySession(){this.activeSession&&(this.activeSession={...this.activeSession,status:"destroyed"},this.activeSession=null)}getActiveSession(){return this.activeSession}}class J{constructor(){this.eventCount=0}incrementEventCount(){this.eventCount+=1}getDiagnostics(e,t,s,i){const a=process.memoryUsage?process.memoryUsage().heapUsed/1048576:0;return{runtimeId:e.runtime.id,lifecycleState:e.runtime.lifecycle,activeServicesCount:t,registeredModulesCount:s,pendingSchedulerTasksCount:i,activeStudentsCount:e.students.length,totalInteractionsCount:e.analytics.totalInteractions,memoryUsageMB:Math.round(a*100)/100,timestamp:Date.now()}}}class K{constructor(e=`rt_${globalThis.crypto.randomUUID()}`){this.currentRole="Teacher",this.runtimeId=e,this.eventBus=new E,this.stateManager=new O(e),this.serviceRegistry=new $,this.moduleRegistry=new B,this.scheduler=new U,this.permissionManager=new T,this.resourceManager=new F,this.snapshotManager=new H,this.hooksManager=new Q,this.sessionManager=new q(this.eventBus,this.stateManager),this.monitor=new J,this.stateManager.setLifecycle("Create")}getContext(){const e=this.sessionManager.getActiveSession();return{runtimeId:this.runtimeId,sessionId:e?.sessionId||"sess_default",courseId:e?.courseId,lessonId:e?.lessonId,teacher:e?.teacher,currentUser:this.currentUser||e?.teacher,role:this.currentRole,permissions:this.permissionManager.getRolePermissions(this.currentRole),lifecycleState:this.stateManager.getState().runtime.lifecycle}}async initialize(){this.transitionState("Initialize"),this.scheduler.start();const e=this.getContext();await this.serviceRegistry.initializeAll(e),this.transitionState("Prepare")}async start(){const e=this.getContext();await this.hooksManager.executeHook("beforeLessonStart",{},e),this.transitionState("Running"),await this.moduleRegistry.startAll(e),await this.hooksManager.executeHook("afterLessonStart",{},e),await this.eventBus.publish("LessonStarted",{lessonId:e.lessonId||"les_active",timestamp:Date.now()}),this.takeSnapshot()}async pause(){this.transitionState("Pause"),this.scheduler.stop(),await this.eventBus.publish("RuntimePaused",{elapsedTime:this.stateManager.getState().runtime.elapsedTime,timestamp:Date.now()})}async resume(){this.transitionState("Resume"),this.scheduler.start(),this.transitionState("Running")}async stop(){const e=this.getContext();this.transitionState("Stop"),await this.moduleRegistry.stopAll(e),this.scheduler.stop(),this.takeSnapshot()}async dispose(){await this.stop(),this.transitionState("Dispose"),await this.serviceRegistry.disposeAll(),this.hooksManager.clear(),this.resourceManager.clear(),this.eventBus.clear()}transitionState(e){const t=this.stateManager.getState().runtime.lifecycle;this.stateManager.setLifecycle(e),this.eventBus.publish("LifecycleChanged",{from:t,to:e,timestamp:Date.now()})}registerModule(e){this.moduleRegistry.registerModule(e)}registerService(e){this.serviceRegistry.registerService(e)}registerRuntimeHook(e,t){return this.hooksManager.registerHook(e,t)}scheduleTask(e,t,s=b.Normal,i=0){return this.scheduler.schedule(e,t,s,i)}setUser(e){this.currentUser=e,this.currentRole=e.role}hasPermission(e){return this.permissionManager.hasPermission(this.currentRole,e)}takeSnapshot(){return this.snapshotManager.takeSnapshot(this.stateManager.getState(),this.serviceRegistry.listServices(),this.moduleRegistry.listModules().map(e=>e.id),this.resourceManager.listResources())}restoreLatestSnapshot(){const e=this.snapshotManager.getLatestSnapshot();return e?(this.stateManager.setState(e.stateTree),!0):!1}getDiagnostics(){return this.monitor.getDiagnostics(this.stateManager.getState(),this.serviceRegistry.listServices().length,this.moduleRegistry.listModules().length,this.scheduler.getPendingTaskCount())}}class V{constructor(e){this.init=e,this.kernel=new K,this.kernel.setUser({id:e.student.id,name:e.student.name,role:"Student",avatar:e.student.avatar,isOnline:!0,joinedAt:Date.now()})}getView(){const e=this.kernel.getContext();return{studentId:this.init.student.id,studentName:this.init.student.name,role:"Student",lessonId:this.init.lessonId??e.lessonId,courseId:this.init.courseId??e.courseId,permissions:e.permissions}}get permissions(){return this.kernel.getContext().permissions}hasPermission(e){return this.kernel.hasPermission(e)}subscribe(e,t){return this.kernel.eventBus.subscribe(e,s=>t(s.payload))}takeSnapshot(){return this.kernel.takeSnapshot()}restoreSnapshot(){return this.kernel.restoreLatestSnapshot()}get runtimeKernel(){return this.kernel}dispose(){return this.kernel.dispose()}}function G({isOpen:n,lang:e,activeStudentId:t,studentNotifications:s,readNotifications:i,setReadNotifications:a,studentDashboardData:c,setIsNotificationsOpen:o,setSelectedNotificationForModal:g}){if(!n)return null;const l=s.filter(u=>!i.has(u.id));return r.jsxs("div",{className:"absolute right-0 mt-2 w-80 bg-white border border-gray-200 shadow-lg rounded-xl z-50 overflow-hidden",children:[r.jsxs("div",{className:"p-3 border-b border-gray-100 flex justify-between items-center bg-gray-50",children:[r.jsx("h3",{className:"font-semibold text-gray-800",children:"Notifications"}),l.length>0&&r.jsx("button",{onClick:async()=>{if(!t)return;try{const h=s.filter(m=>!i.has(m.id)).map(m=>fetch(`/api/students/${t}/read_notifications`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({notificationId:m.id})}));await Promise.all(h)}catch(h){console.error(h)}const u=new Set(i);s.forEach(h=>u.add(h.id)),a(u)},className:"text-xs text-indigo-600 hover:text-indigo-800 font-medium",children:"Mark all as read"})]}),r.jsx("div",{className:"max-h-96 overflow-y-auto",children:s.length===0?r.jsx("div",{className:"p-4 text-center text-sm text-gray-500 italic",children:"No notifications."}):r.jsx("div",{className:"divide-y divide-gray-100",children:s.map(u=>{const h=!i.has(u.id);return r.jsx("div",{className:`p-3 hover:bg-gray-50 cursor-pointer ${h?"bg-indigo-50/30":""}`,onClick:()=>{if(h){t&&fetch(`/api/students/${t}/read_notifications`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({notificationId:u.id})}).catch(console.error);const S=new Set(i);S.add(u.id),a(S)}const m=c?.assignments?.find(S=>S.id===u.relatedId);g({...u,assignment:m}),o(!1)},children:r.jsxs("div",{className:"flex gap-3",children:[r.jsx("div",{className:"mt-0.5",children:u.type==="new_assignment"?r.jsx(C,{size:16,className:"text-indigo-500"}):r.jsx(z,{size:16,className:"text-green-500"})}),r.jsxs("div",{className:"flex-1",children:[r.jsx("div",{className:`text-sm ${h?"font-semibold text-gray-900":"font-medium text-gray-700"}`,children:u.title}),r.jsx("div",{className:`text-xs mt-0.5 ${h?"text-gray-600":"text-gray-500"}`,children:u.message})]}),h&&r.jsx("div",{className:"w-2 h-2 rounded-full bg-indigo-500 mt-1"})]})},u.id)})})})]})}const X=({widgetId:n="widget_ai_teacher_workspace",initialSection:e="Lesson Assistant",onActionExecute:t})=>{const[s,i]=d.useState({widgetId:n,visible:!0,pinned:!0,collapsed:!1,fullscreen:!1,dockPosition:"right",activeSection:e});if(!s.visible)return null;const a=["Lesson Assistant","Whiteboard Assistant","Resource Assistant","Activity Assistant","Student Assistant","Assessment Assistant","Summary Assistant","Plugin Assistant"],c=o=>{t&&t(o,{section:s.activeSection})};return r.jsxs("div",{"data-testid":"ai-teacher-workspace-widget",className:`ai-teacher-workspace-panel ${s.dockPosition} ${s.collapsed?"collapsed":""} ${s.fullscreen?"fullscreen":""}`,style:{border:"1px solid #e2e8f0",borderRadius:"8px",background:"#ffffff",padding:"12px",boxShadow:"0 4px 6px -1px rgba(0, 0, 0, 0.1)"},children:[r.jsxs("div",{className:"ai-widget-header",style:{display:"flex",justifyContent:"space-between",alignItems:"center",borderBottom:"1px solid #f1f5f9",paddingBottom:"8px",marginBottom:"8px"},children:[r.jsx("span",{style:{fontWeight:600,fontSize:"14px",color:"#1e293b"},children:"🤖 AI Teacher Assistant"}),r.jsxs("div",{className:"ai-widget-controls",style:{display:"flex",gap:"4px"},children:[r.jsx("button",{"data-testid":"btn-pin",onClick:()=>i(o=>({...o,pinned:!o.pinned})),style:{fontSize:"12px",cursor:"pointer"},children:s.pinned?"📌 Unpin":"📍 Pin"}),r.jsx("button",{"data-testid":"btn-dock",onClick:()=>i(o=>({...o,dockPosition:o.dockPosition==="right"?"float":"right"})),style:{fontSize:"12px",cursor:"pointer"},children:s.dockPosition==="right"?"🌊 Float":"📥 Dock"}),r.jsx("button",{"data-testid":"btn-fullscreen",onClick:()=>i(o=>({...o,fullscreen:!o.fullscreen})),style:{fontSize:"12px",cursor:"pointer"},children:s.fullscreen?"🗗 Restore":"⛶ Fullscreen"}),r.jsx("button",{"data-testid":"btn-collapse",onClick:()=>i(o=>({...o,collapsed:!o.collapsed})),style:{fontSize:"12px",cursor:"pointer"},children:s.collapsed?"➕ Expand":"➖ Collapse"})]})]}),!s.collapsed&&r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"ai-section-tabs",style:{display:"flex",flexWrap:"wrap",gap:"4px",marginBottom:"12px"},children:a.map(o=>r.jsx("button",{"data-testid":`tab-${o.toLowerCase().replace(/\s+/g,"-")}`,onClick:()=>i(g=>({...g,activeSection:o})),style:{padding:"4px 8px",fontSize:"12px",borderRadius:"4px",border:"1px solid #cbd5e1",background:s.activeSection===o?"#3b82f6":"#f8fafc",color:s.activeSection===o?"#ffffff":"#334155",cursor:"pointer"},children:o},o))}),r.jsxs("div",{className:"ai-section-body",style:{minHeight:"100px"},children:[r.jsxs("div",{style:{fontSize:"13px",color:"#475569",marginBottom:"8px"},children:["Active Assistant: ",r.jsx("strong",{children:s.activeSection})]}),r.jsxs("div",{style:{display:"flex",gap:"8px",flexWrap:"wrap"},children:[r.jsx("button",{"data-testid":"btn-summarize",onClick:()=>c("ai_summarize_lesson"),style:{padding:"6px 12px",fontSize:"12px",background:"#eff6ff",border:"1px solid #bfdbfe",borderRadius:"4px",cursor:"pointer"},children:"📝 Summarize Content"}),r.jsx("button",{"data-testid":"btn-explain",onClick:()=>c("ai_explain_whiteboard"),style:{padding:"6px 12px",fontSize:"12px",background:"#f0fdf4",border:"1px solid #bbf7d0",borderRadius:"4px",cursor:"pointer"},children:"🎨 Explain Visuals"}),r.jsx("button",{"data-testid":"btn-quiz",onClick:()=>c("ai_generate_quiz"),style:{padding:"6px 12px",fontSize:"12px",background:"#fefce8",border:"1px solid #fef08a",borderRadius:"4px",cursor:"pointer"},children:"⚡ Generate Quiz"})]})]})]})]})};class Y{constructor(){this.resources=new Map,this.providers=new Map}registerProvider(e){if(!e||!e.id||!e.type)throw new Error("ResourceRegistry Error: ResourceProvider must have a valid ID and Type.");this.providers.set(e.type,e)}unregisterProvider(e){for(const[t,s]of this.providers.entries())if(s.id===e)return this.providers.delete(t);return!1}getProvider(e){return this.providers.get(e)}registerResource(e){if(!e||!e.id)throw new Error("ResourceRegistry Error: ResourceDescriptor must have a valid ID.");this.resources.set(e.id,{...e})}unregisterResource(e){return this.resources.delete(e)}getResource(e){return this.resources.get(e)}listResources(e){const t=Array.from(this.resources.values());return Object.freeze(e?t.filter(s=>s.type===e):t)}executeAction(e,t,s){const i=this.resources.get(e);if(!i)throw new Error(`ResourceRegistry Error: Resource '${e}' not found.`);const a=this.providers.get(i.type);switch(t){case"preview":return a?.preview?a.preview(i):{previewUrl:i.url};case"open":return a?.open?a.open(i):{openUrl:i.url};case"pin":return i.pinned=!i.pinned,{pinned:i.pinned};case"favorite":return i.favorited=!i.favorited,{favorited:i.favorited};case"annotate":return{annotate:!0,resourceId:e,params:s};case"share":return{shareUrl:i.url??`resource://${i.id}`};case"fullscreen":return{fullscreen:!0,resourceId:e};default:throw new Error(`ResourceRegistry Error: Unknown action '${t}'.`)}}clear(){this.resources.clear(),this.providers.clear()}}const y={padding:"12px",color:"#e2e8f0",fontSize:"13px"},Z=()=>r.jsx("div",{style:{width:"100%",height:"100%"},children:r.jsx(A,{})}),ee=({context:n})=>{const e=n.getView();return r.jsxs("div",{style:y,children:[r.jsx("div",{style:{fontWeight:600,marginBottom:6},children:"📘 Lesson"}),r.jsxs("div",{children:["Lesson ID: ",e.lessonId??"—"]}),r.jsxs("div",{children:["Course ID: ",e.courseId??"—"]})]})},te=()=>{const[n]=d.useState(()=>new Y().listResources());return r.jsxs("div",{style:y,children:[r.jsx("div",{style:{fontWeight:600,marginBottom:6},children:"📂 Resources"}),n.length===0?r.jsx("div",{style:{opacity:.7},children:"No resources shared yet."}):r.jsx("ul",{children:n.map(e=>r.jsxs("li",{children:[e.type,": ",e.url]},e.id))})]})},se=({studentId:n,lang:e="en"})=>r.jsx(P,{role:"student",actorId:n,lang:e}),ie=({studentId:n,lessonId:e,lang:t="en"})=>e?r.jsx(W,{lessonId:e,studentId:n,lang:t,addToast:()=>{}}):r.jsx("div",{style:y,children:"No active lesson — assignments unavailable."}),re=({context:n})=>{const[e,t]=d.useState([]);return d.useEffect(()=>{const s=n.subscribe("LessonStarted",a=>{t(c=>[...c,{id:`ls_${a.timestamp}`,text:`Lesson started (${a.lessonId})`}])}),i=n.subscribe("StageChanged",a=>{t(c=>[...c,{id:`sc_${a.timestamp}`,text:`Stage changed → ${a.toStageId}`}])});return()=>{s(),i()}},[n]),r.jsxs("div",{style:y,children:[r.jsx("div",{style:{fontWeight:600,marginBottom:6},children:"📣 Teacher Broadcast"}),e.length===0?r.jsx("div",{style:{opacity:.7},children:"No announcements yet."}):r.jsx("ul",{children:e.map(s=>r.jsx("li",{children:s.text},s.id))})]})},ne=()=>r.jsx(X,{}),ae=({studentId:n,lang:e="en"})=>{const[t,s]=d.useState(!0),[i,a]=d.useState(new Set),[c]=d.useState([]);return r.jsx(G,{isOpen:t,lang:e,activeStudentId:n,studentNotifications:c,readNotifications:i,setReadNotifications:a,studentDashboardData:null,setIsNotificationsOpen:s,setSelectedNotificationForModal:()=>{}})},oe=()=>r.jsx(I,{slot:"student.view"});class ce{constructor(e){this.registeredWidgets=new Map,this.slotRegistry=e??new w}get slotRegistryRef(){return this.slotRegistry}registerWidget(e){if(!e||!e.id)throw new Error("StudentWidgetRegistry Error: StudentWidgetDescriptor must have a valid ID.");this.registeredWidgets.set(e.id,e);const t=e.component;this.slotRegistry.register({id:e.id,slot:e.slot,priority:e.priority??10,render:s=>x.createElement(t,s)})}unregisterWidget(e){const t=this.registeredWidgets.delete(e);return t&&this.slotRegistry.unregister(e),t}registerDefaultWidgets(e){const{studentId:t,lessonId:s,lang:i="en",context:a,pluginHost:c}=e;this.mountWidget("widget_student_whiteboard","Whiteboard","MainCanvas",Z,{},"official"),this.mountWidget("widget_student_lesson","Lesson","LeftSidebar",ee,{context:a},"official"),this.mountWidget("widget_student_resources","Resources","LeftSidebar",te,{},"official"),this.mountWidget("widget_student_activities","Activities","BottomPanel",se,{},"official"),this.mountWidget("widget_student_assignments","Assignments","RightSidebar",ie,{studentId:t,lessonId:s,lang:i},"official",20),this.mountWidget("widget_student_ai_assistant","AI Learning Assistant","RightSidebar",ne,{},"official",10),this.mountWidget("widget_student_broadcast","Teacher Broadcast","BottomPanel",re,{context:a},"official"),this.mountWidget("widget_student_notifications","Notifications","FloatingArea",ae,{studentId:t,lang:i},"official"),c&&this.mountWidget("widget_student_plugins","Plugin Widgets","FloatingArea",oe,{},"plugin")}getWidget(e){return this.registeredWidgets.get(e)}listWidgets(){return Object.freeze(Array.from(this.registeredWidgets.values()))}clear(){for(const e of this.registeredWidgets.keys())this.slotRegistry.unregister(e);this.registeredWidgets.clear()}mountWidget(e,t,s,i,a,c,o=10){this.registeredWidgets.set(e,{id:e,name:t,slot:s,component:i,provider:c,priority:o}),this.slotRegistry.register({id:e,slot:s,priority:o,render:()=>x.createElement(i,a)})}}const R="openlearn_student_workspace_";function le(n){return{studentId:n,openedWidgets:[],layout:{},selectedResourceIds:[],activityState:{},aiConversation:[],updatedAt:Date.now()}}class de{constructor(e){this.key=`${R}${e}`}load(){try{if(typeof localStorage>"u")return null;const e=localStorage.getItem(this.key);if(!e)return null;const t=JSON.parse(e);return t.studentId!==this.studentIdFromKey()?null:t}catch{return null}}save(e){try{if(typeof localStorage>"u")return;localStorage.setItem(this.key,JSON.stringify({...e,updatedAt:Date.now()}))}catch{}}update(e){const s={...this.load()??le(this.studentIdFromKey()),...e,updatedAt:Date.now()};return this.save(s),s}clear(){try{if(typeof localStorage>"u")return;localStorage.removeItem(this.key)}catch{}}studentIdFromKey(){return this.key.slice(R.length)}}const be=n=>{const{student:e,lessonId:t,courseId:s,teacherId:i,teacherName:a,lang:c="en",autoRestore:o=!0,pluginHost:g}=n,[l]=d.useState(()=>new V({student:e,lessonId:t,courseId:s,teacherId:i,teacherName:a,lang:c})),[u]=d.useState(()=>new w),[h]=d.useState(()=>{const p=new ce(u);return p.registerDefaultWidgets({studentId:e.id,lessonId:t,lang:c,context:l,pluginHost:g}),p}),[m]=d.useState(()=>new de(e.id));d.useEffect(()=>{if(!o)return;l.restoreSnapshot();const p=m.load();p?.currentLessonId&&p.currentLessonId},[o,l,m]),d.useEffect(()=>{const p=l.subscribe("LessonStarted",v=>{m.update({currentLessonId:v.lessonId})});return()=>{p()}},[l,m]),d.useEffect(()=>()=>{l.takeSnapshot();const p=h.listWidgets().map(j=>j.id),v=m.load();m.save({studentId:e.id,openedWidgets:p,layout:v?.layout??{},currentLessonId:v?.currentLessonId??t,selectedResourceIds:v?.selectedResourceIds??[],activityState:v?.activityState??{},aiConversation:v?.aiConversation??[],updatedAt:Date.now()}),l.dispose()},[l,m,h,e.id,t]);const S=r.jsx(N,{registry:u,children:r.jsx(L,{})});return g?r.jsx(M,{host:g,children:S}):S};export{be as StudentWorkspace,be as default};
|
|
1
|
+
import{r as d,j as r,R as x}from"./vendor-react-DHoBZpn2.js";import{E as I,i as M}from"./index-XduPa2-x.js";import{L as A}from"./LazyWhiteboard-CsF9GB6J.js";import{S as W}from"./StudentAssignmentEvalPanel-BRxHMABY.js";import{aq as C,h as z}from"./vendor-icons-DLOPlM4i.js";import{ActivityWorkspaceWidget as P}from"./ActivityWorkspaceWidget-BU88GGUG.js";import"./vendor-utils-BxOuk3id.js";import"./vendor-pdf-DVWGghIL.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-skggaP9D.js";import"./AssignmentFilePreview-D_POvLmv.js";class w{constructor(){this.providers=new Map}register(e){if(!e||!e.id)throw new Error("WorkspaceSlotRegistry Error: WorkspaceSlotProvider must have a valid ID.");this.providers.set(e.id,e)}unregister(e){return this.providers.delete(e)}getProviders(e){const t=Array.from(this.providers.values()).filter(s=>s.slot===e);return t.sort((s,i)=>(i.priority??0)-(s.priority??0)),Object.freeze(t)}listAll(){return Object.freeze(Array.from(this.providers.values()))}clear(){this.providers.clear()}}const k=d.createContext(null),N=({registry:n,children:e})=>{const[t]=d.useState(()=>n??new w),[,s]=d.useState(0),i=d.useCallback(()=>{s(l=>l+1)},[]),a=d.useCallback(l=>{t.register(l),i()},[t,i]),c=d.useCallback(l=>{t.unregister(l)&&i()},[t,i]),o=d.useCallback(l=>t.getProviders(l),[t]),g=d.useMemo(()=>({registry:t,registerSlotProvider:a,unregisterSlotProvider:c,getSlotProviders:o}),[t,a,c,o]);return r.jsx(k.Provider,{value:g,children:e})},_=()=>{const n=d.useContext(k);if(!n)throw new Error("useWorkspace must be used within a WorkspaceProvider");return n},D=n=>{const{getSlotProviders:e}=_();return e(n)},f=({slot:n,className:e})=>{const t=D(n);return t.length===0?null:r.jsx("div",{className:`workspace-slot workspace-slot-${n.toLowerCase()} ${e??""}`,children:t.map(s=>r.jsx(x.Fragment,{children:s.render()},s.id))})},L=({className:n,children:e})=>r.jsxs("div",{className:`workspace-shell flex flex-col h-screen w-screen overflow-hidden bg-slate-900 text-slate-100 ${n??""}`,children:[r.jsx(f,{slot:"TopBar",className:"w-full flex-none z-30"}),r.jsxs("div",{className:"flex flex-1 overflow-hidden relative",children:[r.jsx(f,{slot:"LeftSidebar",className:"flex-none z-20"}),r.jsxs("main",{className:"flex-1 relative overflow-hidden flex flex-col",children:[r.jsx(f,{slot:"MainCanvas",className:"flex-1 w-full h-full relative"}),e,r.jsx(f,{slot:"BottomPanel",className:"flex-none z-20"})]}),r.jsx(f,{slot:"RightSidebar",className:"flex-none z-20"})]}),r.jsx(f,{slot:"StatusBar",className:"w-full flex-none z-30"}),r.jsx(f,{slot:"FloatingArea",className:"absolute inset-0 pointer-events-none z-40"}),r.jsx(f,{slot:"DialogArea",className:"absolute inset-0 pointer-events-none z-50"})]});var b=(n=>(n[n.Immediate=0]="Immediate",n[n.High=1]="High",n[n.Normal=2]="Normal",n[n.Low=3]="Low",n[n.Idle=4]="Idle",n))(b||{});class E{constructor(){this.subscribers=new Map}subscribe(e,t){const s=String(e);this.subscribers.has(s)||this.subscribers.set(s,new Set);const i=this.subscribers.get(s);return i.add(t),()=>{i.delete(t)}}async publish(e,t,s="runtime.system"){const i={id:`evt_${globalThis.crypto.randomUUID()}`,type:e,payload:t,source:s,timestamp:Date.now()},a=this.subscribers.get(String(e))||new Set,c=this.subscribers.get("*")||new Set,o=[...Array.from(a),...Array.from(c)];return await Promise.all(o.map(g=>Promise.resolve(g(i)).catch(l=>{console.error(`[RuntimeEventBus] Error in subscriber for ${String(e)}:`,l)}))),i}clear(){this.subscribers.clear()}}class O{constructor(e=`rt_${globalThis.crypto.randomUUID()}`){this.listeners=new Set,this.currentState={runtime:{id:e,lifecycle:"Create",startTime:Date.now(),elapsedTime:0},lesson:{},stage:{index:0},activity:{},whiteboard:{objectCount:0,isLocked:!1},teachingObjects:[],students:[],plugin:[],ai:{isGenerating:!1},analytics:{totalInteractions:0,activeStudentCount:0,averageScore:0}}}getState(){return this.currentState}setState(e){this.currentState=Object.freeze(e),this.notify()}updateState(e){this.currentState=Object.freeze({...this.currentState,...e,runtime:e.runtime?{...this.currentState.runtime,...e.runtime}:this.currentState.runtime,lesson:e.lesson?{...this.currentState.lesson,...e.lesson}:this.currentState.lesson,stage:e.stage?{...this.currentState.stage,...e.stage}:this.currentState.stage,activity:e.activity?{...this.currentState.activity,...e.activity}:this.currentState.activity,whiteboard:e.whiteboard?{...this.currentState.whiteboard,...e.whiteboard}:this.currentState.whiteboard,ai:e.ai?{...this.currentState.ai,...e.ai}:this.currentState.ai,analytics:e.analytics?{...this.currentState.analytics,...e.analytics}:this.currentState.analytics}),this.notify()}setLifecycle(e){this.updateState({runtime:{...this.currentState.runtime,lifecycle:e}})}addStudent(e){this.currentState.students.some(s=>s.id===e.id)||this.updateState({students:Object.freeze([...this.currentState.students,e])})}removeStudent(e){this.updateState({students:Object.freeze(this.currentState.students.filter(t=>t.id!==e))})}subscribe(e){return this.listeners.add(e),e(this.getState()),()=>{this.listeners.delete(e)}}notify(){const e=this.getState();for(const t of this.listeners)try{t(e)}catch(s){console.error("[RuntimeStateManager] Listener error:",s)}}}class ${constructor(){this.services=new Map}registerService(e){if(!e.serviceId)throw new Error("[RuntimeServiceRegistry] Service must provide a non-empty serviceId.");this.services.set(e.serviceId,e)}async unregisterService(e){const t=this.services.get(e);return t?(await t.dispose(),this.services.delete(e)):!1}getService(e){return this.services.get(e)}listServices(){return Object.freeze(Array.from(this.services.keys()))}async initializeAll(e){for(const t of this.services.values())await t.initialize(e)}async disposeAll(){for(const e of this.services.values())try{await e.dispose()}catch(t){console.error(`[RuntimeServiceRegistry] Error disposing service ${e.serviceId}:`,t)}this.services.clear()}}class B{constructor(){this.modules=new Map,this.activeModuleIds=new Set}registerModule(e){if(!e.id)throw new Error("[RuntimeModuleRegistry] Module must specify a valid id.");this.modules.set(e.id,e)}async unregisterModule(e,t){const s=this.modules.get(e);return s?(this.activeModuleIds.has(e)&&t&&(await s.stop(t),this.activeModuleIds.delete(e)),await s.dispose(),this.modules.delete(e)):!1}getModule(e){return this.modules.get(e)}listModules(){return Object.freeze(Array.from(this.modules.values()))}async startAll(e){for(const t of this.modules.values())await t.initialize(e),await t.start(e),this.activeModuleIds.add(t.id)}async stopAll(e){for(const t of this.activeModuleIds){const s=this.modules.get(t);if(s)try{await s.stop(e)}catch(i){console.error(`[RuntimeModuleRegistry] Error stopping module ${t}:`,i)}}this.activeModuleIds.clear()}}class U{constructor(){this.taskQueue=[],this.isProcessing=!1,this.timer=null}schedule(e,t,s=b.Normal,i=0,a=3){return new Promise((c,o)=>{const g={id:`tsk_${globalThis.crypto.randomUUID()}`,name:e,priority:s,taskFn:async()=>{i>0&&await new Promise(l=>setTimeout(l,i));try{const l=await t();return c(l),l}catch(l){throw o(l),l}},delayMs:i,maxRetries:a,createdAt:Date.now()};this.taskQueue.push(g),this.sortQueue(),queueMicrotask(()=>{this.processQueue()})})}start(){this.timer||(this.timer=setInterval(()=>{this.processQueue()},50))}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}getPendingTaskCount(){return this.taskQueue.length}sortQueue(){this.taskQueue.sort((e,t)=>e.priority-t.priority||e.createdAt-t.createdAt)}async processQueue(){if(this.isProcessing||this.taskQueue.length===0)return;this.isProcessing=!0;const e=this.taskQueue.shift();if(e){let t=0;const s=e.maxRetries??1;for(;t<s;)try{await e.taskFn();break}catch(i){t+=1,t>=s&&console.error(`[RuntimeScheduler] Task ${e.name} (${e.id}) failed after ${t} attempts:`,i)}}this.isProcessing=!1}}class T{constructor(){this.rolePermissions=new Map([["Teacher",new Set(["lesson:control","stage:navigate","whiteboard:draw","quiz:submit","plugin:execute","ai:invoke","session:manage"])],["Assistant",new Set(["stage:navigate","whiteboard:draw","quiz:submit","plugin:execute","ai:invoke"])],["Student",new Set(["whiteboard:draw","quiz:submit","ai:invoke","plugin:execute"])],["Observer",new Set([])],["Plugin",new Set(["whiteboard:draw","plugin:execute","ai:invoke"])],["AI",new Set(["stage:navigate","whiteboard:draw","ai:invoke"])]])}hasPermission(e,t){const s=this.rolePermissions.get(e);return s?s.has(t):!1}grantPermission(e,t){this.rolePermissions.has(e)||this.rolePermissions.set(e,new Set),this.rolePermissions.get(e).add(t)}revokePermission(e,t){const s=this.rolePermissions.get(e);s&&s.delete(t)}getRolePermissions(e){const t=this.rolePermissions.get(e);return Object.freeze(t?Array.from(t):[])}}class F{constructor(){this.resources=new Map}registerResource(e,t,s){const i=Array.from(this.resources.values()).find(c=>c.url===e);if(i)return i;const a={id:`res_${globalThis.crypto.randomUUID()}`,type:t,url:e,sizeBytes:s,status:"loaded",cachedAt:Date.now()};return this.resources.set(a.id,a),a}getResource(e){return this.resources.get(e)}listResources(e){const t=Array.from(this.resources.values());return Object.freeze(e?t.filter(s=>s.type===e):t)}removeResource(e){return this.resources.delete(e)}clear(){this.resources.clear()}}class H{constructor(){this.snapshots=new Map}takeSnapshot(e,t,s,i){const a={snapshotId:`snap_${globalThis.crypto.randomUUID()}`,timestamp:Date.now(),stateTree:JSON.parse(JSON.stringify(e)),activeServices:[...t],loadedModules:[...s],resources:[...i]};return this.snapshots.set(a.snapshotId,a),this.lastAutoSnapshot=a,a}getLatestSnapshot(){return this.lastAutoSnapshot}getSnapshot(e){return this.snapshots.get(e)}clear(){this.snapshots.clear(),this.lastAutoSnapshot=void 0}}class Q{constructor(){this.hooks=new Map}registerHook(e,t){this.hooks.has(e)||this.hooks.set(e,new Set);const s=this.hooks.get(e);return s.add(t),()=>{s.delete(t)}}async executeHook(e,t,s){const i=this.hooks.get(e);if(!(!i||i.size===0))for(const a of i)try{await a(t,s)}catch(c){console.error(`[RuntimeHooksManager] Error executing hook "${e}":`,c)}}clear(){this.hooks.clear()}}class q{constructor(e,t){this.activeSession=null,this.eventBus=e,this.stateManager=t}async createSession(e,t,s,i){this.activeSession&&this.activeSession.status==="active"&&await this.destroySession();const a={sessionId:e,courseId:s,lessonId:i,teacher:t,students:Object.freeze([]),createdAt:Date.now(),status:"active"};return this.activeSession=a,this.stateManager.updateState({lesson:{activeLessonId:i,status:"ready"}}),await this.eventBus.publish("SessionCreated",{sessionId:e,teacherId:t.id,timestamp:Date.now()}),a}async joinSession(e){if(!this.activeSession||this.activeSession.status!=="active")return!1;const t=[...this.activeSession.students.filter(s=>s.id!==e.id),{...e,isOnline:!0}];return this.activeSession={...this.activeSession,students:Object.freeze(t)},this.stateManager.addStudent(e),await this.eventBus.publish("StudentJoined",{student:e,timestamp:Date.now()}),!0}async leaveSession(e){if(!this.activeSession||this.activeSession.status!=="active")return!1;const t=this.activeSession.students.map(s=>s.id===e?{...s,isOnline:!1}:s);return this.activeSession={...this.activeSession,students:Object.freeze(t)},this.stateManager.removeStudent(e),await this.eventBus.publish("StudentLeft",{studentId:e,timestamp:Date.now()}),!0}async reconnectSession(e){if(!this.activeSession||this.activeSession.status!=="active")return!1;const t=this.activeSession.students.find(i=>i.id===e);if(!t)return!1;const s={...t,isOnline:!0};return this.joinSession(s)}async destroySession(){this.activeSession&&(this.activeSession={...this.activeSession,status:"destroyed"},this.activeSession=null)}getActiveSession(){return this.activeSession}}class J{constructor(){this.eventCount=0}incrementEventCount(){this.eventCount+=1}getDiagnostics(e,t,s,i){const a=process.memoryUsage?process.memoryUsage().heapUsed/1048576:0;return{runtimeId:e.runtime.id,lifecycleState:e.runtime.lifecycle,activeServicesCount:t,registeredModulesCount:s,pendingSchedulerTasksCount:i,activeStudentsCount:e.students.length,totalInteractionsCount:e.analytics.totalInteractions,memoryUsageMB:Math.round(a*100)/100,timestamp:Date.now()}}}class K{constructor(e=`rt_${globalThis.crypto.randomUUID()}`){this.currentRole="Teacher",this.runtimeId=e,this.eventBus=new E,this.stateManager=new O(e),this.serviceRegistry=new $,this.moduleRegistry=new B,this.scheduler=new U,this.permissionManager=new T,this.resourceManager=new F,this.snapshotManager=new H,this.hooksManager=new Q,this.sessionManager=new q(this.eventBus,this.stateManager),this.monitor=new J,this.stateManager.setLifecycle("Create")}getContext(){const e=this.sessionManager.getActiveSession();return{runtimeId:this.runtimeId,sessionId:e?.sessionId||"sess_default",courseId:e?.courseId,lessonId:e?.lessonId,teacher:e?.teacher,currentUser:this.currentUser||e?.teacher,role:this.currentRole,permissions:this.permissionManager.getRolePermissions(this.currentRole),lifecycleState:this.stateManager.getState().runtime.lifecycle}}async initialize(){this.transitionState("Initialize"),this.scheduler.start();const e=this.getContext();await this.serviceRegistry.initializeAll(e),this.transitionState("Prepare")}async start(){const e=this.getContext();await this.hooksManager.executeHook("beforeLessonStart",{},e),this.transitionState("Running"),await this.moduleRegistry.startAll(e),await this.hooksManager.executeHook("afterLessonStart",{},e),await this.eventBus.publish("LessonStarted",{lessonId:e.lessonId||"les_active",timestamp:Date.now()}),this.takeSnapshot()}async pause(){this.transitionState("Pause"),this.scheduler.stop(),await this.eventBus.publish("RuntimePaused",{elapsedTime:this.stateManager.getState().runtime.elapsedTime,timestamp:Date.now()})}async resume(){this.transitionState("Resume"),this.scheduler.start(),this.transitionState("Running")}async stop(){const e=this.getContext();this.transitionState("Stop"),await this.moduleRegistry.stopAll(e),this.scheduler.stop(),this.takeSnapshot()}async dispose(){await this.stop(),this.transitionState("Dispose"),await this.serviceRegistry.disposeAll(),this.hooksManager.clear(),this.resourceManager.clear(),this.eventBus.clear()}transitionState(e){const t=this.stateManager.getState().runtime.lifecycle;this.stateManager.setLifecycle(e),this.eventBus.publish("LifecycleChanged",{from:t,to:e,timestamp:Date.now()})}registerModule(e){this.moduleRegistry.registerModule(e)}registerService(e){this.serviceRegistry.registerService(e)}registerRuntimeHook(e,t){return this.hooksManager.registerHook(e,t)}scheduleTask(e,t,s=b.Normal,i=0){return this.scheduler.schedule(e,t,s,i)}setUser(e){this.currentUser=e,this.currentRole=e.role}hasPermission(e){return this.permissionManager.hasPermission(this.currentRole,e)}takeSnapshot(){return this.snapshotManager.takeSnapshot(this.stateManager.getState(),this.serviceRegistry.listServices(),this.moduleRegistry.listModules().map(e=>e.id),this.resourceManager.listResources())}restoreLatestSnapshot(){const e=this.snapshotManager.getLatestSnapshot();return e?(this.stateManager.setState(e.stateTree),!0):!1}getDiagnostics(){return this.monitor.getDiagnostics(this.stateManager.getState(),this.serviceRegistry.listServices().length,this.moduleRegistry.listModules().length,this.scheduler.getPendingTaskCount())}}class V{constructor(e){this.init=e,this.kernel=new K,this.kernel.setUser({id:e.student.id,name:e.student.name,role:"Student",avatar:e.student.avatar,isOnline:!0,joinedAt:Date.now()})}getView(){const e=this.kernel.getContext();return{studentId:this.init.student.id,studentName:this.init.student.name,role:"Student",lessonId:this.init.lessonId??e.lessonId,courseId:this.init.courseId??e.courseId,permissions:e.permissions}}get permissions(){return this.kernel.getContext().permissions}hasPermission(e){return this.kernel.hasPermission(e)}subscribe(e,t){return this.kernel.eventBus.subscribe(e,s=>t(s.payload))}takeSnapshot(){return this.kernel.takeSnapshot()}restoreSnapshot(){return this.kernel.restoreLatestSnapshot()}get runtimeKernel(){return this.kernel}dispose(){return this.kernel.dispose()}}function G({isOpen:n,lang:e,activeStudentId:t,studentNotifications:s,readNotifications:i,setReadNotifications:a,studentDashboardData:c,setIsNotificationsOpen:o,setSelectedNotificationForModal:g}){if(!n)return null;const l=s.filter(u=>!i.has(u.id));return r.jsxs("div",{className:"absolute right-0 mt-2 w-80 bg-white border border-gray-200 shadow-lg rounded-xl z-50 overflow-hidden",children:[r.jsxs("div",{className:"p-3 border-b border-gray-100 flex justify-between items-center bg-gray-50",children:[r.jsx("h3",{className:"font-semibold text-gray-800",children:"Notifications"}),l.length>0&&r.jsx("button",{onClick:async()=>{if(!t)return;try{const h=s.filter(m=>!i.has(m.id)).map(m=>fetch(`/api/students/${t}/read_notifications`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({notificationId:m.id})}));await Promise.all(h)}catch(h){console.error(h)}const u=new Set(i);s.forEach(h=>u.add(h.id)),a(u)},className:"text-xs text-indigo-600 hover:text-indigo-800 font-medium",children:"Mark all as read"})]}),r.jsx("div",{className:"max-h-96 overflow-y-auto",children:s.length===0?r.jsx("div",{className:"p-4 text-center text-sm text-gray-500 italic",children:"No notifications."}):r.jsx("div",{className:"divide-y divide-gray-100",children:s.map(u=>{const h=!i.has(u.id);return r.jsx("div",{className:`p-3 hover:bg-gray-50 cursor-pointer ${h?"bg-indigo-50/30":""}`,onClick:()=>{if(h){t&&fetch(`/api/students/${t}/read_notifications`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({notificationId:u.id})}).catch(console.error);const S=new Set(i);S.add(u.id),a(S)}const m=c?.assignments?.find(S=>S.id===u.relatedId);g({...u,assignment:m}),o(!1)},children:r.jsxs("div",{className:"flex gap-3",children:[r.jsx("div",{className:"mt-0.5",children:u.type==="new_assignment"?r.jsx(C,{size:16,className:"text-indigo-500"}):r.jsx(z,{size:16,className:"text-green-500"})}),r.jsxs("div",{className:"flex-1",children:[r.jsx("div",{className:`text-sm ${h?"font-semibold text-gray-900":"font-medium text-gray-700"}`,children:u.title}),r.jsx("div",{className:`text-xs mt-0.5 ${h?"text-gray-600":"text-gray-500"}`,children:u.message})]}),h&&r.jsx("div",{className:"w-2 h-2 rounded-full bg-indigo-500 mt-1"})]})},u.id)})})})]})}const X=({widgetId:n="widget_ai_teacher_workspace",initialSection:e="Lesson Assistant",onActionExecute:t})=>{const[s,i]=d.useState({widgetId:n,visible:!0,pinned:!0,collapsed:!1,fullscreen:!1,dockPosition:"right",activeSection:e});if(!s.visible)return null;const a=["Lesson Assistant","Whiteboard Assistant","Resource Assistant","Activity Assistant","Student Assistant","Assessment Assistant","Summary Assistant","Plugin Assistant"],c=o=>{t&&t(o,{section:s.activeSection})};return r.jsxs("div",{"data-testid":"ai-teacher-workspace-widget",className:`ai-teacher-workspace-panel ${s.dockPosition} ${s.collapsed?"collapsed":""} ${s.fullscreen?"fullscreen":""}`,style:{border:"1px solid #e2e8f0",borderRadius:"8px",background:"#ffffff",padding:"12px",boxShadow:"0 4px 6px -1px rgba(0, 0, 0, 0.1)"},children:[r.jsxs("div",{className:"ai-widget-header",style:{display:"flex",justifyContent:"space-between",alignItems:"center",borderBottom:"1px solid #f1f5f9",paddingBottom:"8px",marginBottom:"8px"},children:[r.jsx("span",{style:{fontWeight:600,fontSize:"14px",color:"#1e293b"},children:"🤖 AI Teacher Assistant"}),r.jsxs("div",{className:"ai-widget-controls",style:{display:"flex",gap:"4px"},children:[r.jsx("button",{"data-testid":"btn-pin",onClick:()=>i(o=>({...o,pinned:!o.pinned})),style:{fontSize:"12px",cursor:"pointer"},children:s.pinned?"📌 Unpin":"📍 Pin"}),r.jsx("button",{"data-testid":"btn-dock",onClick:()=>i(o=>({...o,dockPosition:o.dockPosition==="right"?"float":"right"})),style:{fontSize:"12px",cursor:"pointer"},children:s.dockPosition==="right"?"🌊 Float":"📥 Dock"}),r.jsx("button",{"data-testid":"btn-fullscreen",onClick:()=>i(o=>({...o,fullscreen:!o.fullscreen})),style:{fontSize:"12px",cursor:"pointer"},children:s.fullscreen?"🗗 Restore":"⛶ Fullscreen"}),r.jsx("button",{"data-testid":"btn-collapse",onClick:()=>i(o=>({...o,collapsed:!o.collapsed})),style:{fontSize:"12px",cursor:"pointer"},children:s.collapsed?"➕ Expand":"➖ Collapse"})]})]}),!s.collapsed&&r.jsxs(r.Fragment,{children:[r.jsx("div",{className:"ai-section-tabs",style:{display:"flex",flexWrap:"wrap",gap:"4px",marginBottom:"12px"},children:a.map(o=>r.jsx("button",{"data-testid":`tab-${o.toLowerCase().replace(/\s+/g,"-")}`,onClick:()=>i(g=>({...g,activeSection:o})),style:{padding:"4px 8px",fontSize:"12px",borderRadius:"4px",border:"1px solid #cbd5e1",background:s.activeSection===o?"#3b82f6":"#f8fafc",color:s.activeSection===o?"#ffffff":"#334155",cursor:"pointer"},children:o},o))}),r.jsxs("div",{className:"ai-section-body",style:{minHeight:"100px"},children:[r.jsxs("div",{style:{fontSize:"13px",color:"#475569",marginBottom:"8px"},children:["Active Assistant: ",r.jsx("strong",{children:s.activeSection})]}),r.jsxs("div",{style:{display:"flex",gap:"8px",flexWrap:"wrap"},children:[r.jsx("button",{"data-testid":"btn-summarize",onClick:()=>c("ai_summarize_lesson"),style:{padding:"6px 12px",fontSize:"12px",background:"#eff6ff",border:"1px solid #bfdbfe",borderRadius:"4px",cursor:"pointer"},children:"📝 Summarize Content"}),r.jsx("button",{"data-testid":"btn-explain",onClick:()=>c("ai_explain_whiteboard"),style:{padding:"6px 12px",fontSize:"12px",background:"#f0fdf4",border:"1px solid #bbf7d0",borderRadius:"4px",cursor:"pointer"},children:"🎨 Explain Visuals"}),r.jsx("button",{"data-testid":"btn-quiz",onClick:()=>c("ai_generate_quiz"),style:{padding:"6px 12px",fontSize:"12px",background:"#fefce8",border:"1px solid #fef08a",borderRadius:"4px",cursor:"pointer"},children:"⚡ Generate Quiz"})]})]})]})]})};class Y{constructor(){this.resources=new Map,this.providers=new Map}registerProvider(e){if(!e||!e.id||!e.type)throw new Error("ResourceRegistry Error: ResourceProvider must have a valid ID and Type.");this.providers.set(e.type,e)}unregisterProvider(e){for(const[t,s]of this.providers.entries())if(s.id===e)return this.providers.delete(t);return!1}getProvider(e){return this.providers.get(e)}registerResource(e){if(!e||!e.id)throw new Error("ResourceRegistry Error: ResourceDescriptor must have a valid ID.");this.resources.set(e.id,{...e})}unregisterResource(e){return this.resources.delete(e)}getResource(e){return this.resources.get(e)}listResources(e){const t=Array.from(this.resources.values());return Object.freeze(e?t.filter(s=>s.type===e):t)}executeAction(e,t,s){const i=this.resources.get(e);if(!i)throw new Error(`ResourceRegistry Error: Resource '${e}' not found.`);const a=this.providers.get(i.type);switch(t){case"preview":return a?.preview?a.preview(i):{previewUrl:i.url};case"open":return a?.open?a.open(i):{openUrl:i.url};case"pin":return i.pinned=!i.pinned,{pinned:i.pinned};case"favorite":return i.favorited=!i.favorited,{favorited:i.favorited};case"annotate":return{annotate:!0,resourceId:e,params:s};case"share":return{shareUrl:i.url??`resource://${i.id}`};case"fullscreen":return{fullscreen:!0,resourceId:e};default:throw new Error(`ResourceRegistry Error: Unknown action '${t}'.`)}}clear(){this.resources.clear(),this.providers.clear()}}const y={padding:"12px",color:"#e2e8f0",fontSize:"13px"},Z=()=>r.jsx("div",{style:{width:"100%",height:"100%"},children:r.jsx(A,{})}),ee=({context:n})=>{const e=n.getView();return r.jsxs("div",{style:y,children:[r.jsx("div",{style:{fontWeight:600,marginBottom:6},children:"📘 Lesson"}),r.jsxs("div",{children:["Lesson ID: ",e.lessonId??"—"]}),r.jsxs("div",{children:["Course ID: ",e.courseId??"—"]})]})},te=()=>{const[n]=d.useState(()=>new Y().listResources());return r.jsxs("div",{style:y,children:[r.jsx("div",{style:{fontWeight:600,marginBottom:6},children:"📂 Resources"}),n.length===0?r.jsx("div",{style:{opacity:.7},children:"No resources shared yet."}):r.jsx("ul",{children:n.map(e=>r.jsxs("li",{children:[e.type,": ",e.url]},e.id))})]})},se=({studentId:n,lang:e="en"})=>r.jsx(P,{role:"student",actorId:n,lang:e}),ie=({studentId:n,lessonId:e,lang:t="en"})=>e?r.jsx(W,{lessonId:e,studentId:n,lang:t,addToast:()=>{}}):r.jsx("div",{style:y,children:"No active lesson — assignments unavailable."}),re=({context:n})=>{const[e,t]=d.useState([]);return d.useEffect(()=>{const s=n.subscribe("LessonStarted",a=>{t(c=>[...c,{id:`ls_${a.timestamp}`,text:`Lesson started (${a.lessonId})`}])}),i=n.subscribe("StageChanged",a=>{t(c=>[...c,{id:`sc_${a.timestamp}`,text:`Stage changed → ${a.toStageId}`}])});return()=>{s(),i()}},[n]),r.jsxs("div",{style:y,children:[r.jsx("div",{style:{fontWeight:600,marginBottom:6},children:"📣 Teacher Broadcast"}),e.length===0?r.jsx("div",{style:{opacity:.7},children:"No announcements yet."}):r.jsx("ul",{children:e.map(s=>r.jsx("li",{children:s.text},s.id))})]})},ne=()=>r.jsx(X,{}),ae=({studentId:n,lang:e="en"})=>{const[t,s]=d.useState(!0),[i,a]=d.useState(new Set),[c]=d.useState([]);return r.jsx(G,{isOpen:t,lang:e,activeStudentId:n,studentNotifications:c,readNotifications:i,setReadNotifications:a,studentDashboardData:null,setIsNotificationsOpen:s,setSelectedNotificationForModal:()=>{}})},oe=()=>r.jsx(I,{slot:"student.view"});class ce{constructor(e){this.registeredWidgets=new Map,this.slotRegistry=e??new w}get slotRegistryRef(){return this.slotRegistry}registerWidget(e){if(!e||!e.id)throw new Error("StudentWidgetRegistry Error: StudentWidgetDescriptor must have a valid ID.");this.registeredWidgets.set(e.id,e);const t=e.component;this.slotRegistry.register({id:e.id,slot:e.slot,priority:e.priority??10,render:s=>x.createElement(t,s)})}unregisterWidget(e){const t=this.registeredWidgets.delete(e);return t&&this.slotRegistry.unregister(e),t}registerDefaultWidgets(e){const{studentId:t,lessonId:s,lang:i="en",context:a,pluginHost:c}=e;this.mountWidget("widget_student_whiteboard","Whiteboard","MainCanvas",Z,{},"official"),this.mountWidget("widget_student_lesson","Lesson","LeftSidebar",ee,{context:a},"official"),this.mountWidget("widget_student_resources","Resources","LeftSidebar",te,{},"official"),this.mountWidget("widget_student_activities","Activities","BottomPanel",se,{},"official"),this.mountWidget("widget_student_assignments","Assignments","RightSidebar",ie,{studentId:t,lessonId:s,lang:i},"official",20),this.mountWidget("widget_student_ai_assistant","AI Learning Assistant","RightSidebar",ne,{},"official",10),this.mountWidget("widget_student_broadcast","Teacher Broadcast","BottomPanel",re,{context:a},"official"),this.mountWidget("widget_student_notifications","Notifications","FloatingArea",ae,{studentId:t,lang:i},"official"),c&&this.mountWidget("widget_student_plugins","Plugin Widgets","FloatingArea",oe,{},"plugin")}getWidget(e){return this.registeredWidgets.get(e)}listWidgets(){return Object.freeze(Array.from(this.registeredWidgets.values()))}clear(){for(const e of this.registeredWidgets.keys())this.slotRegistry.unregister(e);this.registeredWidgets.clear()}mountWidget(e,t,s,i,a,c,o=10){this.registeredWidgets.set(e,{id:e,name:t,slot:s,component:i,provider:c,priority:o}),this.slotRegistry.register({id:e,slot:s,priority:o,render:()=>x.createElement(i,a)})}}const R="openlearn_student_workspace_";function le(n){return{studentId:n,openedWidgets:[],layout:{},selectedResourceIds:[],activityState:{},aiConversation:[],updatedAt:Date.now()}}class de{constructor(e){this.key=`${R}${e}`}load(){try{if(typeof localStorage>"u")return null;const e=localStorage.getItem(this.key);if(!e)return null;const t=JSON.parse(e);return t.studentId!==this.studentIdFromKey()?null:t}catch{return null}}save(e){try{if(typeof localStorage>"u")return;localStorage.setItem(this.key,JSON.stringify({...e,updatedAt:Date.now()}))}catch{}}update(e){const s={...this.load()??le(this.studentIdFromKey()),...e,updatedAt:Date.now()};return this.save(s),s}clear(){try{if(typeof localStorage>"u")return;localStorage.removeItem(this.key)}catch{}}studentIdFromKey(){return this.key.slice(R.length)}}const be=n=>{const{student:e,lessonId:t,courseId:s,teacherId:i,teacherName:a,lang:c="en",autoRestore:o=!0,pluginHost:g}=n,[l]=d.useState(()=>new V({student:e,lessonId:t,courseId:s,teacherId:i,teacherName:a,lang:c})),[u]=d.useState(()=>new w),[h]=d.useState(()=>{const p=new ce(u);return p.registerDefaultWidgets({studentId:e.id,lessonId:t,lang:c,context:l,pluginHost:g}),p}),[m]=d.useState(()=>new de(e.id));d.useEffect(()=>{if(!o)return;l.restoreSnapshot();const p=m.load();p?.currentLessonId&&p.currentLessonId},[o,l,m]),d.useEffect(()=>{const p=l.subscribe("LessonStarted",v=>{m.update({currentLessonId:v.lessonId})});return()=>{p()}},[l,m]),d.useEffect(()=>()=>{l.takeSnapshot();const p=h.listWidgets().map(j=>j.id),v=m.load();m.save({studentId:e.id,openedWidgets:p,layout:v?.layout??{},currentLessonId:v?.currentLessonId??t,selectedResourceIds:v?.selectedResourceIds??[],activityState:v?.activityState??{},aiConversation:v?.aiConversation??[],updatedAt:Date.now()}),l.dispose()},[l,m,h,e.id,t]);const S=r.jsx(N,{registry:u,children:r.jsx(L,{})});return g?r.jsx(M,{host:g,children:S}):S};export{be as StudentWorkspace,be as default};
|
package/dist/env-validation.cjs
CHANGED
|
@@ -307,21 +307,29 @@ function validateStartupEnv(env = process.env) {
|
|
|
307
307
|
if (isProduction) {
|
|
308
308
|
const adminPassword = trimmed(env, "OPENLEARN_ADMIN_PASSWORD");
|
|
309
309
|
if (adminPassword && adminPassword.toLowerCase() === "admin") {
|
|
310
|
-
|
|
310
|
+
warnings.push({
|
|
311
311
|
key: "OPENLEARN_ADMIN_PASSWORD",
|
|
312
|
-
severity: "
|
|
313
|
-
message:
|
|
312
|
+
severity: "warning",
|
|
313
|
+
message: "OPENLEARN_ADMIN_PASSWORD \u4E0E\u7528\u6237\u540D\u76F8\u540C\u3002\u8FD9\u4E0D\u6BD4\u7559\u7A7A\u66F4\u5F31\uFF08\u7559\u7A7A\u65F6\u521D\u59CB\u53E3\u4EE4\u540C\u6837\u662F admin\uFF09\uFF0C\u4F46\u8BF7\u786E\u8BA4\u9996\u6B21\u767B\u5F55\u540E\u7ACB\u5373\u6539\u5BC6\uFF1B\u751F\u4EA7\u90E8\u7F72\u5EFA\u8BAE\u76F4\u63A5\u8BBE\u7F6E\u4E3A\u5F3A\u53E3\u4EE4\u3002"
|
|
314
314
|
});
|
|
315
315
|
}
|
|
316
316
|
const teacherPassword = trimmed(env, "OPENLEARN_TEACHER_PASSWORD");
|
|
317
317
|
if (teacherPassword && teacherPassword.toLowerCase() === "teacher") {
|
|
318
|
-
|
|
318
|
+
warnings.push({
|
|
319
319
|
key: "OPENLEARN_TEACHER_PASSWORD",
|
|
320
|
-
severity: "
|
|
321
|
-
message:
|
|
320
|
+
severity: "warning",
|
|
321
|
+
message: "OPENLEARN_TEACHER_PASSWORD \u4E0E\u7528\u6237\u540D\u76F8\u540C\u3002\u8BF7\u786E\u8BA4\u9996\u6B21\u767B\u5F55\u540E\u7ACB\u5373\u6539\u5BC6\uFF0C\u751F\u4EA7\u90E8\u7F72\u5EFA\u8BAE\u8BBE\u7F6E\u4E3A\u5F3A\u53E3\u4EE4\u3002"
|
|
322
322
|
});
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
const seedMode = trimmed(env, "OPENLEARN_SEED_PASSWORD_MODE");
|
|
326
|
+
if (seedMode && !["default", "random"].includes(seedMode.toLowerCase())) {
|
|
327
|
+
errors.push({
|
|
328
|
+
key: "OPENLEARN_SEED_PASSWORD_MODE",
|
|
329
|
+
severity: "error",
|
|
330
|
+
message: `\u53EA\u652F\u6301 "default"\uFF08\u53E3\u4EE4 = \u7528\u6237\u540D\uFF0C\u9996\u6B21\u767B\u5F55\u5F3A\u5236\u6539\u5BC6\uFF09\u6216 "random"\uFF08\u968F\u673A\u53E3\u4EE4\u5199\u5165 bootstrap-credentials.txt\uFF09\uFF0C\u6536\u5230 "${seedMode}"\u3002`
|
|
331
|
+
});
|
|
332
|
+
}
|
|
325
333
|
const metricsToken = trimmed(env, "METRICS_TOKEN");
|
|
326
334
|
if (metricsToken && metricsToken.length < 16) {
|
|
327
335
|
warnings.push({
|
package/dist/index.html
CHANGED
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
property="og:description"
|
|
14
14
|
content="An AI-powered Learning Management System featuring interactive classrooms, stage management, real-time student response systems, lesson editor, auto-grading, and an extensible third-party plugin ecosystem."
|
|
15
15
|
/>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-XduPa2-x.js"></script>
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/assets/vendor-utils-BxOuk3id.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-DHoBZpn2.js">
|
|
19
19
|
<link rel="modulepreload" crossorigin href="/assets/vendor-icons-DLOPlM4i.js">
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/assets/vendor-motion-BFAPOs-b.js">
|
|
21
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
21
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C4cHQZyx.css">
|
|
22
22
|
</head>
|
|
23
23
|
<body>
|
|
24
24
|
<div id="root"></div>
|