openlearn-next 0.4.2 → 0.7.3

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.
Files changed (149) hide show
  1. package/README.md +26 -21
  2. package/cli-data.mjs +535 -40
  3. package/cli-doctor.mjs +456 -17
  4. package/cli.mjs +214 -12
  5. package/dist/assets/AcademicYearFilter-DUUGpDKt.js +1 -0
  6. package/dist/assets/ActivityWorkspaceWidget-5PzyxF6a.js +1 -0
  7. package/dist/assets/AdminDirectoryView-BCtmQTEK.js +5 -0
  8. package/dist/assets/AssessmentCenter-B43ojHG-.js +1 -0
  9. package/dist/assets/BatchPickerModal-CbmjInp7.js +1 -0
  10. package/dist/assets/ClassesView-DEnMaDOt.js +32 -0
  11. package/dist/assets/ClassroomCountdownWidget-CHq4983w.js +2 -0
  12. package/dist/assets/CloudDriveModal-D2t3Uyzr.js +1 -0
  13. package/dist/assets/ComputerLabView-YtnoT5Rb.js +1 -0
  14. package/dist/assets/CourseManagement-DOXyJ_j-.js +1 -0
  15. package/dist/assets/CourseWizardModal-Dbi6Caw2.js +3 -0
  16. package/dist/assets/CoursewareHubPanel-CyxDIJAF.js +1 -0
  17. package/dist/assets/Dashboard-2rLu_ZBy.js +8 -0
  18. package/dist/assets/ExportWeightModal-BrmAh6VX.js +1 -0
  19. package/dist/assets/GLTFLoader-CZJbo_y1.js +1 -0
  20. package/dist/assets/HelpTour-D_q1z_MI.js +1 -0
  21. package/dist/assets/HelpView-CVBrKDrd.js +204 -0
  22. package/dist/assets/ImportLessonsModal-PhHtX31R.js +1 -0
  23. package/dist/assets/ImportModal-DqjqNeH4.js +2 -0
  24. package/dist/assets/InteractiveCoursewareViewer-Vi6lLz7a.js +1 -0
  25. package/dist/assets/InteractiveWhiteboard-lTzeayX5.js +65 -0
  26. package/dist/assets/LazyCourseware-KIt0rGtW.js +2 -0
  27. package/dist/assets/LazyWhiteboard-CCN3pYcW.js +2 -0
  28. package/dist/assets/LessonEditorView-D_GVhHuM.js +1 -0
  29. package/dist/assets/LiveClassroomView-BRsZ6QEq.js +61 -0
  30. package/dist/assets/NotificationDetailModal-tY5rxYCN.js +1 -0
  31. package/dist/assets/OrbitControls-CQjN_uwS.js +1 -0
  32. package/dist/assets/PluginView-CedpAW6d.js +77 -0
  33. package/dist/assets/ProcessLogsModal-Cfr-T9Gd.js +1 -0
  34. package/dist/assets/QuizGeneratorModal-bUA002kP.js +1 -0
  35. package/dist/assets/StudentAssignmentView-BhAz6Hfk.js +1 -0
  36. package/dist/assets/StudentLessonView-DC1yX4kG.js +1 -0
  37. package/dist/assets/StudentView-PHKxIV9T.js +2 -0
  38. package/dist/assets/SystemResourceLibraryModal-DgXYe6_x.js +1 -0
  39. package/dist/assets/TeacherView-DscJtOJ9.js +2 -0
  40. package/dist/assets/TimetableView-D2ozoj_D.js +6 -0
  41. package/dist/assets/UTIF-FfCsrHhe.js +1 -0
  42. package/dist/assets/__vite-optional-peer-dep_ai_ooxml-ui-BIHI7g3E.js +1 -0
  43. package/dist/assets/browser-DEI0QxZC.js +1 -0
  44. package/dist/assets/html2canvas-pro.esm-Bo50djkB.js +12 -0
  45. package/dist/assets/index-2676X8YB.js +131 -0
  46. package/dist/assets/index-C6hlh8pw.css +1 -0
  47. package/dist/assets/{index.es-C5eWEO4o.js → index.es-DyEF0CZ9.js} +1 -1
  48. package/dist/assets/pluginParsers-e4PSm2Iy.js +1 -0
  49. package/dist/assets/react_pptx-react-viewer-BIHI7g3E.js +1 -0
  50. package/dist/assets/teaching-modes-client-BeVaDdJ-.js +1 -0
  51. package/dist/assets/three.module-iOYB3rD4.js +4228 -0
  52. package/dist/assets/timeline-duration-BgdJCSrW.js +1 -0
  53. package/dist/assets/{vendor-charts-qLbgF1eL.js → vendor-charts-SrpBsvM3.js} +2 -2
  54. package/dist/assets/{vendor-icons-BRmhY6fg.js → vendor-icons-CXrZ3R2V.js} +2 -2
  55. package/dist/assets/{vendor-konva-CEOq4dcl.js → vendor-konva-Dne0Z-WC.js} +1 -1
  56. package/dist/assets/{vendor-pdf-D7rGevI1.js → vendor-pdf-C1Vh2Aqq.js} +56 -56
  57. package/dist/assets/vendor-pptx-Bzn6whfK.js +11311 -0
  58. package/dist/assets/vendor-pptx-DIWdXF1L.css +1 -0
  59. package/dist/assets/{vendor-react-B0t7H3hh.js → vendor-react-Kv_JATwr.js} +1 -1
  60. package/dist/assets/vendor-utils-aM7aYvPP.js +1 -0
  61. package/dist/cli-migrator.cjs +310 -0
  62. package/dist/env-validation.cjs +351 -0
  63. package/dist/index.html +13 -10
  64. package/dist/plugin-net-preload.cjs +263 -0
  65. package/dist/server.cjs +51140 -27229
  66. package/migrations/000_initial_schema.sql +410 -0
  67. package/migrations/001_add_execution_mode.sql +6 -0
  68. package/migrations/002_add_client_session_expiry.sql +6 -0
  69. package/migrations/003_classroom_runtime.sql +33 -0
  70. package/migrations/004_courseware_score_config.sql +26 -0
  71. package/migrations/005_assignment_hub.sql +155 -0
  72. package/migrations/006_classroom_event_bus.sql +28 -0
  73. package/migrations/007_interactive_classroom.sql +119 -0
  74. package/migrations/008_teaching_modes.sql +36 -0
  75. package/migrations/009_classroom_peer_review.sql +159 -0
  76. package/migrations/010_class_groups.sql +33 -0
  77. package/migrations/011_classroom_feed.sql +34 -0
  78. package/migrations/012_auto_record_score.sql +48 -0
  79. package/migrations/013_score_source_strategy.sql +81 -0
  80. package/migrations/014_performance_indexes.sql +21 -0
  81. package/migrations/015_demo_data_registry.sql +26 -0
  82. package/migrations/016_preset_polls_and_passcode_expiry.sql +33 -0
  83. package/migrations/017_pacing_signals_segment.sql +6 -0
  84. package/migrations/018_processes_plugin_owner.sql +26 -0
  85. package/migrations/019_plugins_version.sql +61 -0
  86. package/migrations/020_upload_max_file_mb.sql +14 -0
  87. package/migrations/021_class_owner_and_password_gate.sql +11 -0
  88. package/migrations/022_plugin_public_routes.sql +14 -0
  89. package/migrations/023_encrypt_class_passcodes.sql +18 -0
  90. package/migrations/024_segment_gates.sql +37 -0
  91. package/migrations/025_assignment_lookup_indexes.sql +11 -0
  92. package/migrations/026_plugin_grants.sql +21 -0
  93. package/migrations/027_academic_year.sql +26 -0
  94. package/migrations/028_classroom_indexes.sql +14 -0
  95. package/migrations/029_clean_lesson_think_artifacts.sql +13 -0
  96. package/migrations/030_backfill_academic_year.sql +30 -0
  97. package/migrations/031_assessment_core.sql +158 -0
  98. package/migrations/032_progress_verification.sql +17 -0
  99. package/migrations/033_student_lock_mode.sql +9 -0
  100. package/migrations/034_plugin_orphan_data.sql +27 -0
  101. package/migrations/035_icebreaker_checkins.sql +26 -0
  102. package/migrations/036_student_read_notifications_created_at.sql +19 -0
  103. package/migrations/037_plugin_config_scopes.sql +40 -0
  104. package/migrations/038_assessment_ai_grading_suggestion.sql +10 -0
  105. package/migrations/039_question_bank_sharing_and_papers.sql +14 -0
  106. package/migrations/040_assignment_grade_history.sql +37 -0
  107. package/migrations/041_course_package_idempotency.sql +33 -0
  108. package/migrations/042_assignment_grade_resubmit_audit.sql +12 -0
  109. package/migrations/043_mark_sha256_passwords.sql +31 -0
  110. package/migrations/README.md +61 -0
  111. package/migrations/schema-golden.json +640 -0
  112. package/package.json +54 -28
  113. package/dist/assets/AdminDirectoryView-DY--WM5Z.js +0 -1
  114. package/dist/assets/BatchPickerModal-BpWu0Lil.js +0 -1
  115. package/dist/assets/ClassesView-KbHEqJto.js +0 -22
  116. package/dist/assets/ClassroomCountdownWidget-96WFRO9A.js +0 -2
  117. package/dist/assets/CloudDriveModal-DEQ1nkNk.js +0 -1
  118. package/dist/assets/ComputerLabView-1qWDNRQ2.js +0 -1
  119. package/dist/assets/CourseManagement-D3ZpxY0l.js +0 -1
  120. package/dist/assets/CourseWizardModal-Ck_-M8pk.js +0 -3
  121. package/dist/assets/CoursewareHubPanel-CaEizhxl.js +0 -1
  122. package/dist/assets/Dashboard-CYbpbofV.js +0 -8
  123. package/dist/assets/ExportWeightModal-DW2bu9Zz.js +0 -1
  124. package/dist/assets/HelpTour-CMJbbGne.js +0 -1
  125. package/dist/assets/HelpView-CtzdqhBq.js +0 -204
  126. package/dist/assets/ImportLessonsModal-jBuIK453.js +0 -1
  127. package/dist/assets/ImportModal-D7L-KNhZ.js +0 -1
  128. package/dist/assets/InteractiveCoursewareViewer-Bj7ahXMs.js +0 -1
  129. package/dist/assets/InteractiveWhiteboard-B7JcJvDu.js +0 -77
  130. package/dist/assets/LazyCourseware-CuiPxhDg.js +0 -2
  131. package/dist/assets/LazyWhiteboard-BJwCEZAU.js +0 -2
  132. package/dist/assets/LessonEditorView-DFrOp2qp.js +0 -1
  133. package/dist/assets/LiveClassroomView-BZ5nGee3.js +0 -59
  134. package/dist/assets/NotificationDetailModal-Baqnc5pU.js +0 -1
  135. package/dist/assets/PluginView-CCbIuZ24.js +0 -66
  136. package/dist/assets/ProcessLogsModal-DmFXg9mG.js +0 -1
  137. package/dist/assets/QuizGeneratorModal-BBEDssp7.js +0 -1
  138. package/dist/assets/StudentAssignmentView-DgIjUsXz.js +0 -1
  139. package/dist/assets/StudentLessonView-CBJbI5x6.js +0 -1
  140. package/dist/assets/StudentPreviewModal-Bh8OZs0t.js +0 -1
  141. package/dist/assets/StudentView--uGxc5TJ.js +0 -2
  142. package/dist/assets/SystemResourceLibraryModal-BVCclcqA.js +0 -1
  143. package/dist/assets/TeacherView-D4Zsc0eZ.js +0 -2
  144. package/dist/assets/TimetableView-BQG-rEb5.js +0 -6
  145. package/dist/assets/index-BKBCBKvy.js +0 -217
  146. package/dist/assets/index-CvGu7LSj.css +0 -1
  147. package/dist/assets/purify.es-CYR4BTuT.js +0 -3
  148. package/dist/assets/vendor-pptx-C5-U7ieS.js +0 -39
  149. package/dist/assets/vendor-utils-m0Urvtws.js +0 -1
@@ -0,0 +1 @@
1
+ import{TrianglesDrawMode as ye,TriangleFanDrawMode as Q,TriangleStripDrawMode as de,Loader as we,LoaderUtils as v,FileLoader as pe,MeshPhysicalMaterial as I,Vector2 as me,Color as F,LinearSRGBColorSpace as N,SRGBColorSpace as U,SpotLight as be,PointLight as Ie,DirectionalLight as Ne,Matrix4 as B,InstancedMesh as Oe,Vector3 as k,Quaternion as ge,InstancedBufferAttribute as ne,BufferGeometry as Te,Object3D as Re,TextureLoader as Ce,ImageBitmapLoader as ke,BufferAttribute as V,InterleavedBuffer as Fe,LinearMipmapLinearFilter as Ee,NearestMipmapLinearFilter as Pe,LinearMipmapNearestFilter as He,NearestMipmapNearestFilter as De,LinearFilter as J,NearestFilter as xe,RepeatWrapping as Z,MirroredRepeatWrapping as Ge,ClampToEdgeWrapping as ve,PointsMaterial as Ue,Material as X,LineBasicMaterial as Be,MeshStandardMaterial as Ae,DoubleSide as Ke,MeshBasicMaterial as G,PropertyBinding as je,SkinnedMesh as Ve,Mesh as Xe,LineSegments as ze,Line as qe,LineLoop as We,Points as Ye,Group as z,PerspectiveCamera as Qe,MathUtils as Je,OrthographicCamera as Ze,Skeleton as $e,AnimationClip as en,Bone as nn,InterpolateDiscrete as tn,InterpolateLinear as _e,InterleavedBufferAttribute as sn,Texture as te,VectorKeyframeTrack as se,NumberKeyframeTrack as ie,QuaternionKeyframeTrack as re,ColorManagement as oe,FrontSide as rn,Interpolant as on,Box3 as an,Sphere as cn}from"./three.module-iOYB3rD4.js";function ae(f,n){if(n===ye)return console.warn("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles."),f;if(n===Q||n===de){let e=f.getIndex();if(e===null){const i=[],o=f.getAttribute("position");if(o!==void 0){for(let a=0;a<o.count;a++)i.push(a);f.setIndex(i),e=f.getIndex()}else return console.error("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible."),f}const s=e.count-2,t=[];if(n===Q)for(let i=1;i<=s;i++)t.push(e.getX(0)),t.push(e.getX(i)),t.push(e.getX(i+1));else for(let i=0;i<s;i++)i%2===0?(t.push(e.getX(i)),t.push(e.getX(i+1)),t.push(e.getX(i+2))):(t.push(e.getX(i+2)),t.push(e.getX(i+1)),t.push(e.getX(i)));return t.length/3!==s&&console.error("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles."),f.setIndex(t),f.clearGroups(),f}else return console.error("THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:",n),f}function un(f){const n=new Map,e=new Map,s=f.clone();return Me(f,s,function(t,i){n.set(i,t),e.set(t,i)}),s.traverse(function(t){if(!t.isSkinnedMesh)return;const i=t,o=n.get(t),a=o.skeleton.bones;i.skeleton=o.skeleton.clone(),i.bindMatrix.copy(o.bindMatrix),i.skeleton.bones=a.map(function(r){return e.get(r)}),i.bind(i.skeleton,i.bindMatrix)}),s}function Me(f,n,e){e(f,n);for(let s=0;s<f.children.length;s++)Me(f.children[s],n.children[s],e)}class Vn extends we{constructor(n){super(n),this.dracoLoader=null,this.ktx2Loader=null,this.meshoptDecoder=null,this.pluginCallbacks=[],this.register(function(e){return new pn(e)}),this.register(function(e){return new mn(e)}),this.register(function(e){return new Ln(e)}),this.register(function(e){return new Sn(e)}),this.register(function(e){return new yn(e)}),this.register(function(e){return new Tn(e)}),this.register(function(e){return new Rn(e)}),this.register(function(e){return new En(e)}),this.register(function(e){return new xn(e)}),this.register(function(e){return new dn(e)}),this.register(function(e){return new An(e)}),this.register(function(e){return new gn(e)}),this.register(function(e){return new Mn(e)}),this.register(function(e){return new _n(e)}),this.register(function(e){return new fn(e)}),this.register(function(e){return new ce(e,g.EXT_MESHOPT_COMPRESSION)}),this.register(function(e){return new ce(e,g.KHR_MESHOPT_COMPRESSION)}),this.register(function(e){return new wn(e)})}load(n,e,s,t){const i=this;let o;if(this.resourcePath!=="")o=this.resourcePath;else if(this.path!==""){const c=v.extractUrlBase(n);o=v.resolveURL(c,this.path)}else o=v.extractUrlBase(n);this.manager.itemStart(n);const a=function(c){t?t(c):console.error(c),i.manager.itemError(n),i.manager.itemEnd(n)},r=new pe(this.manager);r.setPath(this.path),r.setResponseType("arraybuffer"),r.setRequestHeader(this.requestHeader),r.setWithCredentials(this.withCredentials),r.load(n,function(c){try{i.parse(c,o,function(u){e(u),i.manager.itemEnd(n)},a)}catch(u){a(u)}},s,a)}setDRACOLoader(n){return this.dracoLoader=n,this}setKTX2Loader(n){return this.ktx2Loader=n,this}setMeshoptDecoder(n){return this.meshoptDecoder=n,this}register(n){return this.pluginCallbacks.indexOf(n)===-1&&this.pluginCallbacks.push(n),this}unregister(n){return this.pluginCallbacks.indexOf(n)!==-1&&this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(n),1),this}parse(n,e,s,t){let i;const o={},a={},r=new TextDecoder;if(typeof n=="string")i=JSON.parse(n);else if(n instanceof ArrayBuffer)if(r.decode(new Uint8Array(n,0,4))===Le){try{o[g.KHR_BINARY_GLTF]=new bn(n)}catch(l){t&&t(l);return}i=JSON.parse(o[g.KHR_BINARY_GLTF].content)}else i=JSON.parse(r.decode(n));else i=n;if(i.asset===void 0||i.asset.version[0]<2){t&&t(new Error("THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported."));return}const c=new Bn(i,{path:e||this.resourcePath||"",crossOrigin:this.crossOrigin,requestHeader:this.requestHeader,manager:this.manager,ktx2Loader:this.ktx2Loader,meshoptDecoder:this.meshoptDecoder});c.fileLoader.setRequestHeader(this.requestHeader);for(let u=0;u<this.pluginCallbacks.length;u++){const l=this.pluginCallbacks[u](c);l.name||console.error("THREE.GLTFLoader: Invalid plugin found: missing name"),a[l.name]=l,o[l.name]=!0}if(i.extensionsUsed)for(let u=0;u<i.extensionsUsed.length;++u){const l=i.extensionsUsed[u],h=i.extensionsRequired||[];switch(l){case g.KHR_MATERIALS_UNLIT:o[l]=new hn;break;case g.KHR_DRACO_MESH_COMPRESSION:o[l]=new In(i,this.dracoLoader);break;case g.KHR_TEXTURE_TRANSFORM:o[l]=new Nn;break;case g.KHR_MESH_QUANTIZATION:o[l]=new On;break;default:h.indexOf(l)>=0&&a[l]===void 0&&console.warn('THREE.GLTFLoader: Unknown extension "'+l+'".')}}c.setExtensions(o),c.setPlugins(a),c.parse(s,t)}parseAsync(n,e){const s=this;return new Promise(function(t,i){s.parse(n,e,t,i)})}}function ln(){let f={};return{get:function(n){return f[n]},add:function(n,e){f[n]=e},remove:function(n){delete f[n]},removeAll:function(){f={}}}}function L(f,n,e){const s=f.json.materials[n];return s.extensions&&s.extensions[e]?s.extensions[e]:null}const g={KHR_BINARY_GLTF:"KHR_binary_glTF",KHR_DRACO_MESH_COMPRESSION:"KHR_draco_mesh_compression",KHR_LIGHTS_PUNCTUAL:"KHR_lights_punctual",KHR_MATERIALS_CLEARCOAT:"KHR_materials_clearcoat",KHR_MATERIALS_DISPERSION:"KHR_materials_dispersion",KHR_MATERIALS_IOR:"KHR_materials_ior",KHR_MATERIALS_SHEEN:"KHR_materials_sheen",KHR_MATERIALS_SPECULAR:"KHR_materials_specular",KHR_MATERIALS_TRANSMISSION:"KHR_materials_transmission",KHR_MATERIALS_IRIDESCENCE:"KHR_materials_iridescence",KHR_MATERIALS_ANISOTROPY:"KHR_materials_anisotropy",KHR_MATERIALS_UNLIT:"KHR_materials_unlit",KHR_MATERIALS_VOLUME:"KHR_materials_volume",KHR_TEXTURE_BASISU:"KHR_texture_basisu",KHR_TEXTURE_TRANSFORM:"KHR_texture_transform",KHR_MESH_QUANTIZATION:"KHR_mesh_quantization",KHR_MATERIALS_EMISSIVE_STRENGTH:"KHR_materials_emissive_strength",EXT_MATERIALS_BUMP:"EXT_materials_bump",EXT_TEXTURE_WEBP:"EXT_texture_webp",EXT_TEXTURE_AVIF:"EXT_texture_avif",EXT_MESHOPT_COMPRESSION:"EXT_meshopt_compression",KHR_MESHOPT_COMPRESSION:"KHR_meshopt_compression",EXT_MESH_GPU_INSTANCING:"EXT_mesh_gpu_instancing"};class fn{constructor(n){this.parser=n,this.name=g.KHR_LIGHTS_PUNCTUAL,this.cache={refs:{},uses:{}}}_markDefs(){const n=this.parser,e=this.parser.json.nodes||[];for(let s=0,t=e.length;s<t;s++){const i=e[s];i.extensions&&i.extensions[this.name]&&i.extensions[this.name].light!==void 0&&n._addNodeRef(this.cache,i.extensions[this.name].light)}}_loadLight(n){const e=this.parser,s="light:"+n;let t=e.cache.get(s);if(t)return t;const i=e.json,r=((i.extensions&&i.extensions[this.name]||{}).lights||[])[n];let c;const u=new F(16777215);r.color!==void 0&&u.setRGB(r.color[0],r.color[1],r.color[2],N);const l=r.range!==void 0?r.range:0;switch(r.type){case"directional":c=new Ne(u),c.target.position.set(0,0,-1),c.add(c.target);break;case"point":c=new Ie(u),c.distance=l;break;case"spot":c=new be(u),c.distance=l,r.spot=r.spot||{},r.spot.innerConeAngle=r.spot.innerConeAngle!==void 0?r.spot.innerConeAngle:0,r.spot.outerConeAngle=r.spot.outerConeAngle!==void 0?r.spot.outerConeAngle:Math.PI/4,c.angle=r.spot.outerConeAngle,c.penumbra=1-r.spot.innerConeAngle/r.spot.outerConeAngle,c.target.position.set(0,0,-1),c.add(c.target);break;default:throw new Error("THREE.GLTFLoader: Unexpected light type: "+r.type)}return c.position.set(0,0,0),b(c,r),r.intensity!==void 0&&(c.intensity=r.intensity),c.name=e.createUniqueName(r.name||"light_"+n),t=Promise.resolve(c),e.cache.add(s,t),t}getDependency(n,e){if(n==="light")return this._loadLight(e)}createNodeAttachment(n){const e=this,s=this.parser,i=s.json.nodes[n],a=(i.extensions&&i.extensions[this.name]||{}).light;return a===void 0?null:this._loadLight(a).then(function(r){return s._getNodeRef(e.cache,a,r)})}}class hn{constructor(){this.name=g.KHR_MATERIALS_UNLIT}getMaterialType(){return G}extendParams(n,e,s){const t=[];n.color=new F(1,1,1),n.opacity=1;const i=e.pbrMetallicRoughness;if(i){if(Array.isArray(i.baseColorFactor)){const o=i.baseColorFactor;n.color.setRGB(o[0],o[1],o[2],N),n.opacity=o[3]}i.baseColorTexture!==void 0&&t.push(s.assignTexture(n,"map",i.baseColorTexture,U))}return Promise.all(t)}}class dn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_EMISSIVE_STRENGTH}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);return s===null||s.emissiveStrength!==void 0&&(e.emissiveIntensity=s.emissiveStrength),Promise.resolve()}}class pn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_CLEARCOAT}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];if(s.clearcoatFactor!==void 0&&(e.clearcoat=s.clearcoatFactor),s.clearcoatTexture!==void 0&&t.push(this.parser.assignTexture(e,"clearcoatMap",s.clearcoatTexture)),s.clearcoatRoughnessFactor!==void 0&&(e.clearcoatRoughness=s.clearcoatRoughnessFactor),s.clearcoatRoughnessTexture!==void 0&&t.push(this.parser.assignTexture(e,"clearcoatRoughnessMap",s.clearcoatRoughnessTexture)),s.clearcoatNormalTexture!==void 0&&(t.push(this.parser.assignTexture(e,"clearcoatNormalMap",s.clearcoatNormalTexture)),s.clearcoatNormalTexture.scale!==void 0)){const i=s.clearcoatNormalTexture.scale;e.clearcoatNormalScale=new me(i,i)}return Promise.all(t)}}class mn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_DISPERSION}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);return s===null||(e.dispersion=s.dispersion!==void 0?s.dispersion:0),Promise.resolve()}}class gn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_IRIDESCENCE}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];return s.iridescenceFactor!==void 0&&(e.iridescence=s.iridescenceFactor),s.iridescenceTexture!==void 0&&t.push(this.parser.assignTexture(e,"iridescenceMap",s.iridescenceTexture)),s.iridescenceIor!==void 0&&(e.iridescenceIOR=s.iridescenceIor),e.iridescenceThicknessRange===void 0&&(e.iridescenceThicknessRange=[100,400]),s.iridescenceThicknessMinimum!==void 0&&(e.iridescenceThicknessRange[0]=s.iridescenceThicknessMinimum),s.iridescenceThicknessMaximum!==void 0&&(e.iridescenceThicknessRange[1]=s.iridescenceThicknessMaximum),s.iridescenceThicknessTexture!==void 0&&t.push(this.parser.assignTexture(e,"iridescenceThicknessMap",s.iridescenceThicknessTexture)),Promise.all(t)}}class Tn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_SHEEN}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];if(e.sheenColor=new F(0,0,0),e.sheenRoughness=0,e.sheen=1,s.sheenColorFactor!==void 0){const i=s.sheenColorFactor;e.sheenColor.setRGB(i[0],i[1],i[2],N)}return s.sheenRoughnessFactor!==void 0&&(e.sheenRoughness=s.sheenRoughnessFactor),s.sheenColorTexture!==void 0&&t.push(this.parser.assignTexture(e,"sheenColorMap",s.sheenColorTexture,U)),s.sheenRoughnessTexture!==void 0&&t.push(this.parser.assignTexture(e,"sheenRoughnessMap",s.sheenRoughnessTexture)),Promise.all(t)}}class Rn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_TRANSMISSION}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];return s.transmissionFactor!==void 0&&(e.transmission=s.transmissionFactor),s.transmissionTexture!==void 0&&t.push(this.parser.assignTexture(e,"transmissionMap",s.transmissionTexture)),Promise.all(t)}}class En{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_VOLUME}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];e.thickness=s.thicknessFactor!==void 0?s.thicknessFactor:0,s.thicknessTexture!==void 0&&t.push(this.parser.assignTexture(e,"thicknessMap",s.thicknessTexture)),e.attenuationDistance=s.attenuationDistance||1/0;const i=s.attenuationColor||[1,1,1];return e.attenuationColor=new F().setRGB(i[0],i[1],i[2],N),Promise.all(t)}}class xn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_IOR}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);return s===null||(e.ior=s.ior!==void 0?s.ior:1.5,e.ior===0&&(e.ior=1e3)),Promise.resolve()}}class An{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_SPECULAR}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];e.specularIntensity=s.specularFactor!==void 0?s.specularFactor:1,s.specularTexture!==void 0&&t.push(this.parser.assignTexture(e,"specularIntensityMap",s.specularTexture));const i=s.specularColorFactor||[1,1,1];return e.specularColor=new F().setRGB(i[0],i[1],i[2],N),s.specularColorTexture!==void 0&&t.push(this.parser.assignTexture(e,"specularColorMap",s.specularColorTexture,U)),Promise.all(t)}}class _n{constructor(n){this.parser=n,this.name=g.EXT_MATERIALS_BUMP}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];return e.bumpScale=s.bumpFactor!==void 0?s.bumpFactor:1,s.bumpTexture!==void 0&&t.push(this.parser.assignTexture(e,"bumpMap",s.bumpTexture)),Promise.all(t)}}class Mn{constructor(n){this.parser=n,this.name=g.KHR_MATERIALS_ANISOTROPY}getMaterialType(n){return L(this.parser,n,this.name)!==null?I:null}extendMaterialParams(n,e){const s=L(this.parser,n,this.name);if(s===null)return Promise.resolve();const t=[];return s.anisotropyStrength!==void 0&&(e.anisotropy=s.anisotropyStrength),s.anisotropyRotation!==void 0&&(e.anisotropyRotation=s.anisotropyRotation),s.anisotropyTexture!==void 0&&t.push(this.parser.assignTexture(e,"anisotropyMap",s.anisotropyTexture)),Promise.all(t)}}class Ln{constructor(n){this.parser=n,this.name=g.KHR_TEXTURE_BASISU}loadTexture(n){const e=this.parser,s=e.json,t=s.textures[n];if(!t.extensions||!t.extensions[this.name])return null;const i=t.extensions[this.name],o=e.options.ktx2Loader;if(!o){if(s.extensionsRequired&&s.extensionsRequired.indexOf(this.name)>=0)throw new Error("THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures");return null}return e.loadTextureImage(n,i.source,o)}}class Sn{constructor(n){this.parser=n,this.name=g.EXT_TEXTURE_WEBP}loadTexture(n){const e=this.name,s=this.parser,t=s.json,i=t.textures[n];if(!i.extensions||!i.extensions[e])return null;const o=i.extensions[e],a=t.images[o.source];let r=s.textureLoader;if(a.uri){const c=s.options.manager.getHandler(a.uri);c!==null&&(r=c)}return s.loadTextureImage(n,o.source,r)}}class yn{constructor(n){this.parser=n,this.name=g.EXT_TEXTURE_AVIF}loadTexture(n){const e=this.name,s=this.parser,t=s.json,i=t.textures[n];if(!i.extensions||!i.extensions[e])return null;const o=i.extensions[e],a=t.images[o.source];let r=s.textureLoader;if(a.uri){const c=s.options.manager.getHandler(a.uri);c!==null&&(r=c)}return s.loadTextureImage(n,o.source,r)}}class ce{constructor(n,e){this.name=e,this.parser=n}loadBufferView(n){const e=this.parser.json,s=e.bufferViews[n];if(s.extensions&&s.extensions[this.name]){const t=s.extensions[this.name],i=this.parser.getDependency("buffer",t.buffer),o=this.parser.options.meshoptDecoder;if(!o||!o.supported){if(e.extensionsRequired&&e.extensionsRequired.indexOf(this.name)>=0)throw new Error("THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files");return null}return i.then(function(a){const r=t.byteOffset||0,c=t.byteLength||0,u=t.count,l=t.byteStride,h=new Uint8Array(a,r,c);return o.decodeGltfBufferAsync?o.decodeGltfBufferAsync(u,l,h,t.mode,t.filter).then(function(d){return d.buffer}):o.ready.then(function(){const d=new ArrayBuffer(u*l);return o.decodeGltfBuffer(new Uint8Array(d),u,l,h,t.mode,t.filter),d})})}else return null}}class wn{constructor(n){this.name=g.EXT_MESH_GPU_INSTANCING,this.parser=n}createNodeMesh(n){const e=this.parser.json,s=e.nodes[n];if(!s.extensions||!s.extensions[this.name]||s.mesh===void 0)return null;const t=e.meshes[s.mesh];for(const c of t.primitives)if(c.mode!==w.TRIANGLES&&c.mode!==w.TRIANGLE_STRIP&&c.mode!==w.TRIANGLE_FAN&&c.mode!==void 0)return null;const o=s.extensions[this.name].attributes,a=[],r={};for(const c in o)a.push(this.parser.getDependency("accessor",o[c]).then(u=>(r[c]=u,r[c])));return a.length<1?null:(a.push(this.parser.createNodeMesh(n)),Promise.all(a).then(c=>{const u=c.pop(),l=u.isGroup?u.children:[u],h=c[0].count,d=[];for(const p of l){const T=new B,m=new k,R=new ge,M=new k(1,1,1),A=new Oe(p.geometry,p.material,h);for(let _=0;_<h;_++)r.TRANSLATION&&m.fromBufferAttribute(r.TRANSLATION,_),r.ROTATION&&R.fromBufferAttribute(r.ROTATION,_),r.SCALE&&M.fromBufferAttribute(r.SCALE,_),A.setMatrixAt(_,T.compose(m,R,M));let E=null;for(const _ in r)if(_==="_COLOR_0"){const S=r[_];A.instanceColor=new ne(S.array,S.itemSize,S.normalized)}else if(_!=="TRANSLATION"&&_!=="ROTATION"&&_!=="SCALE"){if(E===null){const x=A.geometry;E=new Te,E.name=x.name;for(const y in x.attributes)E.setAttribute(y,x.attributes[y]);for(const y in x.morphAttributes)E.morphAttributes[y]=x.morphAttributes[y];x.index!==null&&E.setIndex(x.index),E.morphTargetsRelative=x.morphTargetsRelative;for(const y of x.groups)E.addGroup(y.start,y.count,y.materialIndex);x.boundingBox!==null&&(E.boundingBox=x.boundingBox.clone()),x.boundingSphere!==null&&(E.boundingSphere=x.boundingSphere.clone()),E.drawRange.start=x.drawRange.start,E.drawRange.count=x.drawRange.count,E.userData=Object.assign({},x.userData),A.geometry=E}const S=r[_];E.setAttribute(_,new ne(S.array,S.itemSize,S.normalized))}Re.prototype.copy.call(A,p),this.parser.assignFinalMaterial(A),d.push(A)}return u.isGroup?(u.clear(),u.add(...d),u):d[0]}))}}const Le="glTF",D=12,ue={JSON:1313821514,BIN:5130562};class bn{constructor(n){this.name=g.KHR_BINARY_GLTF,this.content=null,this.body=null;const e=new DataView(n,0,D),s=new TextDecoder;if(this.header={magic:s.decode(new Uint8Array(n.slice(0,4))),version:e.getUint32(4,!0),length:e.getUint32(8,!0)},this.header.magic!==Le)throw new Error("THREE.GLTFLoader: Unsupported glTF-Binary header.");if(this.header.version<2)throw new Error("THREE.GLTFLoader: Legacy binary file detected.");const t=this.header.length-D,i=new DataView(n,D);let o=0;for(;o<t;){const a=i.getUint32(o,!0);o+=4;const r=i.getUint32(o,!0);if(o+=4,r===ue.JSON){const c=new Uint8Array(n,D+o,a);this.content=s.decode(c)}else if(r===ue.BIN){const c=D+o;this.body=n.slice(c,c+a)}o+=a}if(this.content===null)throw new Error("THREE.GLTFLoader: JSON content not found.")}}class In{constructor(n,e){if(!e)throw new Error("THREE.GLTFLoader: No DRACOLoader instance provided.");this.name=g.KHR_DRACO_MESH_COMPRESSION,this.json=n,this.dracoLoader=e,this.dracoLoader.preload()}decodePrimitive(n,e){const s=this.json,t=this.dracoLoader,i=n.extensions[this.name].bufferView,o=n.extensions[this.name].attributes,a={},r={},c={};for(const u in o){const l=$[u]||u.toLowerCase();a[l]=o[u]}for(const u in n.attributes){const l=$[u]||u.toLowerCase();if(o[u]!==void 0){const h=s.accessors[n.attributes[u]],d=H[h.componentType];c[l]=d.name,r[l]=h.normalized===!0}}return e.getDependency("bufferView",i).then(function(u){return new Promise(function(l,h){t.decodeDracoFile(u,function(d){for(const p in d.attributes){const T=d.attributes[p],m=r[p];m!==void 0&&(T.normalized=m)}l(d)},a,c,N,h)})})}}class Nn{constructor(){this.name=g.KHR_TEXTURE_TRANSFORM}extendTexture(n,e){if((e.texCoord===void 0||e.texCoord===n.channel)&&e.offset===void 0&&e.rotation===void 0&&e.scale===void 0)return n;if(n=n.clone(),e.texCoord!==void 0&&(n.channel=e.texCoord),e.offset!==void 0&&n.offset.fromArray(e.offset),e.rotation!==void 0&&(n.rotation=e.rotation),e.scale!==void 0&&n.repeat.fromArray(e.scale),e.rotation!==void 0){const s=Math.cos(n.rotation),t=Math.sin(n.rotation);n.matrix.set(n.repeat.x*s,n.repeat.y*t,n.offset.x,-n.repeat.x*t,n.repeat.y*s,n.offset.y,0,0,1),n.matrixAutoUpdate=!1}return n.needsUpdate=!0,n}}class On{constructor(){this.name=g.KHR_MESH_QUANTIZATION}}class Se extends on{constructor(n,e,s,t){super(n,e,s,t)}copySampleValue_(n){const e=this.resultBuffer,s=this.sampleValues,t=this.valueSize,i=n*t*3+t;for(let o=0;o!==t;o++)e[o]=s[i+o];return e}interpolate_(n,e,s,t){const i=this.resultBuffer,o=this.sampleValues,a=this.valueSize,r=a*2,c=a*3,u=t-e,l=(s-e)/u,h=l*l,d=h*l,p=n*c,T=p-c,m=-2*d+3*h,R=d-h,M=1-m,A=R-h+l;for(let E=0;E!==a;E++){const _=o[T+E+a],S=o[T+E+r]*u,x=o[p+E+a],y=o[p+E]*u;i[E]=M*_+A*S+m*x+R*y}return i}}const Cn=new ge;class kn extends Se{interpolate_(n,e,s,t){const i=super.interpolate_(n,e,s,t);return Cn.fromArray(i).normalize().toArray(i),i}}const w={POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6},H={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array},le={9728:xe,9729:J,9984:De,9985:He,9986:Pe,9987:Ee},fe={33071:ve,33648:Ge,10497:Z},q={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16},$={POSITION:"position",NORMAL:"normal",TANGENT:"tangent",TEXCOORD_0:"uv",TEXCOORD_1:"uv1",TEXCOORD_2:"uv2",TEXCOORD_3:"uv3",COLOR_0:"color",WEIGHTS_0:"skinWeight",JOINTS_0:"skinIndex"},O={scale:"scale",translation:"position",rotation:"quaternion",weights:"morphTargetInfluences"},Fn={CUBICSPLINE:void 0,LINEAR:_e,STEP:tn},W={OPAQUE:"OPAQUE",MASK:"MASK",BLEND:"BLEND"};function Pn(f){return f.DefaultMaterial===void 0&&(f.DefaultMaterial=new Ae({color:16777215,emissive:0,metalness:1,roughness:1,transparent:!1,depthTest:!0,side:rn})),f.DefaultMaterial}function C(f,n,e){for(const s in e.extensions)f[s]===void 0&&(n.userData.gltfExtensions=n.userData.gltfExtensions||{},n.userData.gltfExtensions[s]=e.extensions[s])}function b(f,n){n.extras!==void 0&&(typeof n.extras=="object"?Object.assign(f.userData,n.extras):console.warn("THREE.GLTFLoader: Ignoring primitive type .extras, "+n.extras))}function Hn(f,n,e){let s=!1,t=!1,i=!1;for(let c=0,u=n.length;c<u;c++){const l=n[c];if(l.POSITION!==void 0&&(s=!0),l.NORMAL!==void 0&&(t=!0),l.COLOR_0!==void 0&&(i=!0),s&&t&&i)break}if(!s&&!t&&!i)return Promise.resolve(f);const o=[],a=[],r=[];for(let c=0,u=n.length;c<u;c++){const l=n[c];if(s){const h=l.POSITION!==void 0?e.getDependency("accessor",l.POSITION):f.attributes.position;o.push(h)}if(t){const h=l.NORMAL!==void 0?e.getDependency("accessor",l.NORMAL):f.attributes.normal;a.push(h)}if(i){const h=l.COLOR_0!==void 0?e.getDependency("accessor",l.COLOR_0):f.attributes.color;r.push(h)}}return Promise.all([Promise.all(o),Promise.all(a),Promise.all(r)]).then(function(c){const u=c[0],l=c[1],h=c[2];return s&&(f.morphAttributes.position=u),t&&(f.morphAttributes.normal=l),i&&(f.morphAttributes.color=h),f.morphTargetsRelative=!0,f})}function Dn(f,n){if(f.updateMorphTargets(),n.weights!==void 0)for(let e=0,s=n.weights.length;e<s;e++)f.morphTargetInfluences[e]=n.weights[e];if(n.extras&&Array.isArray(n.extras.targetNames)){const e=n.extras.targetNames;if(f.morphTargetInfluences.length===e.length){f.morphTargetDictionary={};for(let s=0,t=e.length;s<t;s++)f.morphTargetDictionary[e[s]]=s}else console.warn("THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.")}}function Gn(f){let n;const e=f.extensions&&f.extensions[g.KHR_DRACO_MESH_COMPRESSION];if(e?n="draco:"+e.bufferView+":"+e.indices+":"+Y(e.attributes):n=f.indices+":"+Y(f.attributes)+":"+f.mode,f.targets!==void 0)for(let s=0,t=f.targets.length;s<t;s++)n+=":"+Y(f.targets[s]);return n}function Y(f){let n="";const e=Object.keys(f).sort();for(let s=0,t=e.length;s<t;s++)n+=e[s]+":"+f[e[s]]+";";return n}function ee(f){switch(f){case Int8Array:return 1/127;case Uint8Array:return 1/255;case Int16Array:return 1/32767;case Uint16Array:return 1/65535;default:throw new Error("THREE.GLTFLoader: Unsupported normalized accessor component type.")}}function vn(f){return f.search(/\.jpe?g($|\?)/i)>0||f.search(/^data\:image\/jpeg/)===0?"image/jpeg":f.search(/\.webp($|\?)/i)>0||f.search(/^data\:image\/webp/)===0?"image/webp":f.search(/\.ktx2($|\?)/i)>0||f.search(/^data\:image\/ktx2/)===0?"image/ktx2":"image/png"}const Un=new B;class Bn{constructor(n={},e={}){this.json=n,this.extensions={},this.plugins={},this.options=e,this.cache=new ln,this.associations=new Map,this.primitiveCache={},this.nodeCache={},this.meshCache={refs:{},uses:{}},this.cameraCache={refs:{},uses:{}},this.lightCache={refs:{},uses:{}},this.sourceCache={},this.textureCache={},this.nodeNamesUsed={};let s=!1,t=-1,i=!1,o=-1;if(typeof navigator<"u"&&typeof navigator.userAgent<"u"){const a=navigator.userAgent;s=/^((?!chrome|android).)*safari/i.test(a)===!0;const r=a.match(/Version\/(\d+)/);t=s&&r?parseInt(r[1],10):-1,i=a.indexOf("Firefox")>-1,o=i?a.match(/Firefox\/([0-9]+)\./)[1]:-1}typeof createImageBitmap>"u"||s&&t<17||i&&o<98?this.textureLoader=new Ce(this.options.manager):this.textureLoader=new ke(this.options.manager),this.textureLoader.setCrossOrigin(this.options.crossOrigin),this.textureLoader.setRequestHeader(this.options.requestHeader),this.fileLoader=new pe(this.options.manager),this.fileLoader.setResponseType("arraybuffer"),this.options.crossOrigin==="use-credentials"&&this.fileLoader.setWithCredentials(!0)}setExtensions(n){this.extensions=n}setPlugins(n){this.plugins=n}parse(n,e){const s=this,t=this.json,i=this.extensions;this.cache.removeAll(),this.nodeCache={},this._invokeAll(function(o){return o._markDefs&&o._markDefs()}),Promise.all(this._invokeAll(function(o){return o.beforeRoot&&o.beforeRoot()})).then(function(){return Promise.all([s.getDependencies("scene"),s.getDependencies("animation"),s.getDependencies("camera")])}).then(function(o){const a={scene:o[0][t.scene||0],scenes:o[0],animations:o[1],cameras:o[2],asset:t.asset,parser:s,userData:{}};return C(i,a,t),b(a,t),Promise.all(s._invokeAll(function(r){return r.afterRoot&&r.afterRoot(a)})).then(function(){for(const r of a.scenes)r.updateMatrixWorld();n(a)})}).catch(e)}_markDefs(){const n=this.json.nodes||[],e=this.json.skins||[],s=this.json.meshes||[];for(let t=0,i=e.length;t<i;t++){const o=e[t].joints;for(let a=0,r=o.length;a<r;a++)n[o[a]].isBone=!0}for(let t=0,i=n.length;t<i;t++){const o=n[t];o.mesh!==void 0&&(this._addNodeRef(this.meshCache,o.mesh),o.skin!==void 0&&(s[o.mesh].isSkinnedMesh=!0)),o.camera!==void 0&&this._addNodeRef(this.cameraCache,o.camera)}}_addNodeRef(n,e){e!==void 0&&(n.refs[e]===void 0&&(n.refs[e]=n.uses[e]=0),n.refs[e]++)}_getNodeRef(n,e,s){if(n.refs[e]<=1)return s;const t=s.clone(),i=(o,a)=>{const r=this.associations.get(o);r!=null&&this.associations.set(a,r);for(const[c,u]of o.children.entries())i(u,a.children[c])};return i(s,t),t.name+="_instance_"+n.uses[e]++,t}_invokeOne(n){const e=Object.values(this.plugins);e.push(this);for(let s=0;s<e.length;s++){const t=n(e[s]);if(t)return t}return null}_invokeAll(n){const e=Object.values(this.plugins);e.unshift(this);const s=[];for(let t=0;t<e.length;t++){const i=n(e[t]);i&&s.push(i)}return s}getDependency(n,e){const s=n+":"+e;let t=this.cache.get(s);if(!t){switch(n){case"scene":t=this.loadScene(e);break;case"node":t=this._invokeOne(function(i){return i.loadNode&&i.loadNode(e)});break;case"mesh":t=this._invokeOne(function(i){return i.loadMesh&&i.loadMesh(e)});break;case"accessor":t=this.loadAccessor(e);break;case"bufferView":t=this._invokeOne(function(i){return i.loadBufferView&&i.loadBufferView(e)});break;case"buffer":t=this.loadBuffer(e);break;case"material":t=this._invokeOne(function(i){return i.loadMaterial&&i.loadMaterial(e)});break;case"texture":t=this._invokeOne(function(i){return i.loadTexture&&i.loadTexture(e)});break;case"skin":t=this.loadSkin(e);break;case"animation":t=this._invokeOne(function(i){return i.loadAnimation&&i.loadAnimation(e)});break;case"camera":t=this.loadCamera(e);break;default:if(t=this._invokeOne(function(i){return i!=this&&i.getDependency&&i.getDependency(n,e)}),!t)throw new Error("Unknown type: "+n);break}this.cache.add(s,t)}return t}getDependencies(n){let e=this.cache.get(n);if(!e){const s=this,t=this.json[n+(n==="mesh"?"es":"s")]||[];e=Promise.all(t.map(function(i,o){return s.getDependency(n,o)})),this.cache.add(n,e)}return e}loadBuffer(n){const e=this.json.buffers[n],s=this.fileLoader;if(e.type&&e.type!=="arraybuffer")throw new Error("THREE.GLTFLoader: "+e.type+" buffer type is not supported.");if(e.uri===void 0&&n===0)return Promise.resolve(this.extensions[g.KHR_BINARY_GLTF].body);const t=this.options;return new Promise(function(i,o){s.load(v.resolveURL(e.uri,t.path),i,void 0,function(){o(new Error('THREE.GLTFLoader: Failed to load buffer "'+e.uri+'".'))})})}loadBufferView(n){const e=this.json.bufferViews[n];return this.getDependency("buffer",e.buffer).then(function(s){const t=e.byteLength||0,i=e.byteOffset||0;return s.slice(i,i+t)})}loadAccessor(n){const e=this,s=this.json,t=this.json.accessors[n];if(t.bufferView===void 0&&t.sparse===void 0){const o=q[t.type],a=H[t.componentType],r=t.normalized===!0,c=new a(t.count*o);return Promise.resolve(new V(c,o,r))}const i=[];return t.bufferView!==void 0?i.push(this.getDependency("bufferView",t.bufferView)):i.push(null),t.sparse!==void 0&&(i.push(this.getDependency("bufferView",t.sparse.indices.bufferView)),i.push(this.getDependency("bufferView",t.sparse.values.bufferView))),Promise.all(i).then(function(o){const a=o[0],r=q[t.type],c=H[t.componentType],u=c.BYTES_PER_ELEMENT,l=u*r,h=t.byteOffset||0,d=t.bufferView!==void 0?s.bufferViews[t.bufferView].byteStride:void 0,p=t.normalized===!0;let T,m;if(d&&d!==l){const R=Math.floor(h/d),M="InterleavedBuffer:"+t.bufferView+":"+t.componentType+":"+R+":"+t.count;let A=e.cache.get(M);A||(T=new c(a,R*d,t.count*d/u),A=new Fe(T,d/u),e.cache.add(M,A)),m=new sn(A,r,h%d/u,p)}else a===null?T=new c(t.count*r):T=new c(a,h,t.count*r),m=new V(T,r,p);if(t.sparse!==void 0){const R=q.SCALAR,M=H[t.sparse.indices.componentType],A=t.sparse.indices.byteOffset||0,E=t.sparse.values.byteOffset||0,_=new M(o[1],A,t.sparse.count*R),S=new c(o[2],E,t.sparse.count*r);a!==null&&(m=new V(m.array.slice(),m.itemSize,m.normalized)),m.normalized=!1;for(let x=0,y=_.length;x<y;x++){const P=_[x];if(m.setX(P,S[x*r]),r>=2&&m.setY(P,S[x*r+1]),r>=3&&m.setZ(P,S[x*r+2]),r>=4&&m.setW(P,S[x*r+3]),r>=5)throw new Error("THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.")}m.normalized=p}return m})}loadTexture(n){const e=this.json,s=this.options,i=e.textures[n].source,o=e.images[i];let a=this.textureLoader;if(o.uri){const r=s.manager.getHandler(o.uri);r!==null&&(a=r)}return this.loadTextureImage(n,i,a)}loadTextureImage(n,e,s){const t=this,i=this.json,o=i.textures[n],a=i.images[e],r=(a.uri||a.bufferView)+":"+o.sampler;if(this.textureCache[r])return this.textureCache[r];const c=this.loadImageSource(e,s).then(function(u){u.flipY=!1,u.name=o.name||a.name||"",u.name===""&&typeof a.uri=="string"&&a.uri.startsWith("data:image/")===!1&&(u.name=a.uri);const h=(i.samplers||{})[o.sampler]||{};return u.magFilter=le[h.magFilter]||J,u.minFilter=le[h.minFilter]||Ee,u.wrapS=fe[h.wrapS]||Z,u.wrapT=fe[h.wrapT]||Z,u.generateMipmaps=!u.isCompressedTexture&&u.minFilter!==xe&&u.minFilter!==J,t.associations.set(u,{textures:n}),u}).catch(function(){return null});return this.textureCache[r]=c,c}loadImageSource(n,e){const s=this,t=this.json,i=this.options;if(this.sourceCache[n]!==void 0)return this.sourceCache[n].then(l=>l.clone());const o=t.images[n],a=self.URL||self.webkitURL;let r=o.uri||"",c=!1;if(o.bufferView!==void 0)r=s.getDependency("bufferView",o.bufferView).then(function(l){c=!0;const h=new Blob([l],{type:o.mimeType});return r=a.createObjectURL(h),r});else if(o.uri===void 0)throw new Error("THREE.GLTFLoader: Image "+n+" is missing URI and bufferView");const u=Promise.resolve(r).then(function(l){return new Promise(function(h,d){let p=h;e.isImageBitmapLoader===!0&&(p=function(T){const m=new te(T);m.needsUpdate=!0,h(m)}),e.load(v.resolveURL(l,i.path),p,void 0,d)})}).then(function(l){return c===!0&&a.revokeObjectURL(r),b(l,o),l.userData.mimeType=o.mimeType||vn(o.uri),l}).catch(function(l){throw console.error("THREE.GLTFLoader: Couldn't load texture",r),l});return this.sourceCache[n]=u,u}assignTexture(n,e,s,t){const i=this;return this.getDependency("texture",s.index).then(function(o){if(!o)return null;if(s.texCoord!==void 0&&s.texCoord>0&&(o=o.clone(),o.channel=s.texCoord),i.extensions[g.KHR_TEXTURE_TRANSFORM]){const a=s.extensions!==void 0?s.extensions[g.KHR_TEXTURE_TRANSFORM]:void 0;if(a){const r=i.associations.get(o);o=i.extensions[g.KHR_TEXTURE_TRANSFORM].extendTexture(o,a),i.associations.set(o,r)}}return t!==void 0&&(o.colorSpace=t),n[e]=o,o})}assignFinalMaterial(n){const e=n.geometry;let s=n.material;const t=e.attributes.tangent===void 0,i=e.attributes.color!==void 0,o=e.attributes.normal===void 0;if(n.isPoints){const a="PointsMaterial:"+s.uuid;let r=this.cache.get(a);r||(r=new Ue,X.prototype.copy.call(r,s),r.color.copy(s.color),r.map=s.map,r.sizeAttenuation=!1,this.cache.add(a,r)),s=r}else if(n.isLine){const a="LineBasicMaterial:"+s.uuid;let r=this.cache.get(a);r||(r=new Be,X.prototype.copy.call(r,s),r.color.copy(s.color),r.map=s.map,this.cache.add(a,r)),s=r}if(t||i||o){let a="ClonedMaterial:"+s.uuid+":";t&&(a+="derivative-tangents:"),i&&(a+="vertex-colors:"),o&&(a+="flat-shading:");let r=this.cache.get(a);r||(r=s.clone(),i&&(r.vertexColors=!0),o&&(r.flatShading=!0),t&&(r.normalScale&&(r.normalScale.y*=-1),r.clearcoatNormalScale&&(r.clearcoatNormalScale.y*=-1)),this.cache.add(a,r),this.associations.set(r,this.associations.get(s))),s=r}n.material=s}getMaterialType(){return Ae}loadMaterial(n){const e=this,s=this.json,t=this.extensions,i=s.materials[n];let o;const a={},r=i.extensions||{},c=[];if(r[g.KHR_MATERIALS_UNLIT]){const l=t[g.KHR_MATERIALS_UNLIT];o=l.getMaterialType(),c.push(l.extendParams(a,i,e))}else{const l=i.pbrMetallicRoughness||{};if(a.color=new F(1,1,1),a.opacity=1,Array.isArray(l.baseColorFactor)){const h=l.baseColorFactor;a.color.setRGB(h[0],h[1],h[2],N),a.opacity=h[3]}l.baseColorTexture!==void 0&&c.push(e.assignTexture(a,"map",l.baseColorTexture,U)),a.metalness=l.metallicFactor!==void 0?l.metallicFactor:1,a.roughness=l.roughnessFactor!==void 0?l.roughnessFactor:1,l.metallicRoughnessTexture!==void 0&&(c.push(e.assignTexture(a,"metalnessMap",l.metallicRoughnessTexture)),c.push(e.assignTexture(a,"roughnessMap",l.metallicRoughnessTexture))),o=this._invokeOne(function(h){return h.getMaterialType&&h.getMaterialType(n)}),c.push(Promise.all(this._invokeAll(function(h){return h.extendMaterialParams&&h.extendMaterialParams(n,a)})))}i.doubleSided===!0&&(a.side=Ke);const u=i.alphaMode||W.OPAQUE;if(u===W.BLEND?(a.transparent=!0,a.depthWrite=!1):(a.transparent=!1,u===W.MASK&&(a.alphaTest=i.alphaCutoff!==void 0?i.alphaCutoff:.5)),i.normalTexture!==void 0&&o!==G&&(c.push(e.assignTexture(a,"normalMap",i.normalTexture)),a.normalScale=new me(1,1),i.normalTexture.scale!==void 0)){const l=i.normalTexture.scale;a.normalScale.set(l,l)}if(i.occlusionTexture!==void 0&&o!==G&&(c.push(e.assignTexture(a,"aoMap",i.occlusionTexture)),i.occlusionTexture.strength!==void 0&&(a.aoMapIntensity=i.occlusionTexture.strength)),i.emissiveFactor!==void 0&&o!==G){const l=i.emissiveFactor;a.emissive=new F().setRGB(l[0],l[1],l[2],N)}return i.emissiveTexture!==void 0&&o!==G&&c.push(e.assignTexture(a,"emissiveMap",i.emissiveTexture,U)),Promise.all(c).then(function(){const l=new o(a);return i.name&&(l.name=i.name),b(l,i),e.associations.set(l,{materials:n}),i.extensions&&C(t,l,i),l})}createUniqueName(n){const e=je.sanitizeNodeName(n||"");return e in this.nodeNamesUsed?e+"_"+ ++this.nodeNamesUsed[e]:(this.nodeNamesUsed[e]=0,e)}loadGeometries(n){const e=this,s=this.extensions,t=this.primitiveCache;function i(a){return s[g.KHR_DRACO_MESH_COMPRESSION].decodePrimitive(a,e).then(function(r){return he(r,a,e)})}const o=[];for(let a=0,r=n.length;a<r;a++){const c=n[a],u=Gn(c),l=t[u];if(l)o.push(l.promise);else{let h;c.extensions&&c.extensions[g.KHR_DRACO_MESH_COMPRESSION]?h=i(c):h=he(new Te,c,e),c.mode===w.TRIANGLE_STRIP?h=h.then(d=>ae(d,de)):c.mode===w.TRIANGLE_FAN&&(h=h.then(d=>ae(d,Q))),t[u]={primitive:c,promise:h},o.push(h)}}return Promise.all(o)}loadMesh(n){const e=this,s=this.json,t=this.extensions,i=s.meshes[n],o=i.primitives,a=[];for(let r=0,c=o.length;r<c;r++){const u=o[r].material===void 0?Pn(this.cache):this.getDependency("material",o[r].material);a.push(u)}return a.push(e.loadGeometries(o)),Promise.all(a).then(async function(r){const c=r.slice(0,r.length-1),u=r[r.length-1],l=[];for(let d=0,p=u.length;d<p;d++){const T=u[d],m=o[d];let R;const M=c[d];if(m.mode===w.TRIANGLES||m.mode===w.TRIANGLE_STRIP||m.mode===w.TRIANGLE_FAN||m.mode===void 0){const A=i.isSkinnedMesh===!0,E=T.hasAttribute("skinIndex")&&T.hasAttribute("skinWeight");A&&E===!1&&console.warn("THREE.GLTFLoader: Missing skinIndex or skinWeight attributes. Skinning disabled."),R=A&&E?new Ve(T,M):new Xe(T,M),R.isSkinnedMesh===!0&&R.normalizeSkinWeights()}else if(m.mode===w.LINES)R=new ze(T,M);else if(m.mode===w.LINE_STRIP)R=new qe(T,M);else if(m.mode===w.LINE_LOOP)R=new We(T,M);else if(m.mode===w.POINTS)R=new Ye(T,M);else throw new Error("THREE.GLTFLoader: Primitive mode unsupported: "+m.mode);Object.keys(R.geometry.morphAttributes).length>0&&Dn(R,i),R.name=e.createUniqueName(i.name||"mesh_"+n),b(R,i),m.extensions&&C(t,R,m),e.assignFinalMaterial(R),l.push(R)}for(let d=0,p=l.length;d<p;d++)e.associations.set(l[d],{meshes:n,primitives:d});if(l.length===1)return i.extensions&&C(t,l[0],i),l[0];const h=new z;i.extensions&&C(t,h,i),e.associations.set(h,{meshes:n});for(let d=0,p=l.length;d<p;d++)h.add(l[d]);return h})}loadCamera(n){let e;const s=this.json.cameras[n],t=s[s.type];if(!t){console.warn("THREE.GLTFLoader: Missing camera parameters.");return}return s.type==="perspective"?e=new Qe(Je.radToDeg(t.yfov),t.aspectRatio||1,t.znear||1,t.zfar||2e6):s.type==="orthographic"&&(e=new Ze(-t.xmag,t.xmag,t.ymag,-t.ymag,t.znear,t.zfar)),s.name&&(e.name=this.createUniqueName(s.name)),b(e,s),Promise.resolve(e)}loadSkin(n){const e=this.json.skins[n],s=[];for(let t=0,i=e.joints.length;t<i;t++)s.push(this._loadNodeShallow(e.joints[t]));return e.inverseBindMatrices!==void 0?s.push(this.getDependency("accessor",e.inverseBindMatrices)):s.push(null),Promise.all(s).then(function(t){const i=t.pop(),o=t,a=[],r=[];for(let c=0,u=o.length;c<u;c++){const l=o[c];if(l){a.push(l);const h=new B;i!==null&&h.fromArray(i.array,c*16),r.push(h)}else console.warn('THREE.GLTFLoader: Joint "%s" could not be found.',e.joints[c])}return new $e(a,r)})}loadAnimation(n){const e=this.json,s=this,t=e.animations[n],i=t.name?t.name:"animation_"+n,o=[],a=[],r=[],c=[],u=[];for(let l=0,h=t.channels.length;l<h;l++){const d=t.channels[l],p=t.samplers[d.sampler],T=d.target,m=T.node,R=t.parameters!==void 0?t.parameters[p.input]:p.input,M=t.parameters!==void 0?t.parameters[p.output]:p.output;T.node!==void 0&&(o.push(this.getDependency("node",m)),a.push(this.getDependency("accessor",R)),r.push(this.getDependency("accessor",M)),c.push(p),u.push(T))}return Promise.all([Promise.all(o),Promise.all(a),Promise.all(r),Promise.all(c),Promise.all(u)]).then(function(l){const h=l[0],d=l[1],p=l[2],T=l[3],m=l[4],R=[];for(let A=0,E=h.length;A<E;A++){const _=h[A],S=d[A],x=p[A],y=T[A],P=m[A];if(_===void 0)continue;_.updateMatrix&&_.updateMatrix();const K=s._createAnimationTracks(_,S,x,y,P);if(K)for(let j=0;j<K.length;j++)R.push(K[j])}const M=new en(i,void 0,R);return b(M,t),M})}createNodeMesh(n){const e=this.json,s=this,t=e.nodes[n];return t.mesh===void 0?null:s.getDependency("mesh",t.mesh).then(function(i){const o=s._getNodeRef(s.meshCache,t.mesh,i);return t.weights!==void 0&&o.traverse(function(a){if(a.isMesh)for(let r=0,c=t.weights.length;r<c;r++)a.morphTargetInfluences[r]=t.weights[r]}),o})}loadNode(n){const e=this.json,s=this,t=e.nodes[n],i=s._loadNodeShallow(n),o=[],a=t.children||[];for(let c=0,u=a.length;c<u;c++)o.push(s.getDependency("node",a[c]));const r=t.skin===void 0?Promise.resolve(null):s.getDependency("skin",t.skin);return Promise.all([i,Promise.all(o),r]).then(function(c){const u=c[0],l=c[1],h=c[2];h!==null&&u.traverse(function(d){d.isSkinnedMesh&&d.bind(h,Un)});for(let d=0,p=l.length;d<p;d++)u.add(l[d]);if(u.userData.pivot!==void 0&&l.length>0){const d=u.userData.pivot,p=l[0];u.pivot=new k().fromArray(d),u.position.x-=d[0],u.position.y-=d[1],u.position.z-=d[2],p.position.set(0,0,0),delete u.userData.pivot}return u})}_loadNodeShallow(n){const e=this.json,s=this.extensions,t=this;if(this.nodeCache[n]!==void 0)return this.nodeCache[n];const i=e.nodes[n],o=i.name?t.createUniqueName(i.name):"",a=[],r=t._invokeOne(function(c){return c.createNodeMesh&&c.createNodeMesh(n)});return r&&a.push(r),i.camera!==void 0&&a.push(t.getDependency("camera",i.camera).then(function(c){return t._getNodeRef(t.cameraCache,i.camera,c)})),t._invokeAll(function(c){return c.createNodeAttachment&&c.createNodeAttachment(n)}).forEach(function(c){a.push(c)}),this.nodeCache[n]=Promise.all(a).then(function(c){let u;if(i.isBone===!0?u=new nn:c.length>1?u=new z:c.length===1?u=c[0]:u=new Re,u!==c[0])for(let l=0,h=c.length;l<h;l++)u.add(c[l]);if(i.name&&(u.userData.name=i.name,u.name=o),b(u,i),i.extensions&&C(s,u,i),i.matrix!==void 0){const l=new B;l.fromArray(i.matrix),u.applyMatrix4(l)}else i.translation!==void 0&&u.position.fromArray(i.translation),i.rotation!==void 0&&u.quaternion.fromArray(i.rotation),i.scale!==void 0&&u.scale.fromArray(i.scale);if(!t.associations.has(u))t.associations.set(u,{});else if(i.mesh!==void 0&&t.meshCache.refs[i.mesh]>1){const l=t.associations.get(u);t.associations.set(u,{...l})}return t.associations.get(u).nodes=n,u}),this.nodeCache[n]}loadScene(n){const e=this.extensions,s=this.json.scenes[n],t=this,i=new z;s.name&&(i.name=t.createUniqueName(s.name)),b(i,s),s.extensions&&C(e,i,s);const o=s.nodes||[],a=[];for(let r=0,c=o.length;r<c;r++)a.push(t.getDependency("node",o[r]));return Promise.all(a).then(function(r){for(let u=0,l=r.length;u<l;u++){const h=r[u];h.parent!==null?i.add(un(h)):i.add(h)}const c=u=>{const l=new Map;for(const[h,d]of t.associations)(h instanceof X||h instanceof te)&&l.set(h,d);return u.traverse(h=>{const d=t.associations.get(h);d!=null&&l.set(h,d)}),l};return t.associations=c(i),i})}_createAnimationTracks(n,e,s,t,i){const o=[],a=n.name?n.name:n.uuid,r=[];function c(d){d.morphTargetInfluences&&r.push(d.name?d.name:d.uuid)}O[i.path]===O.weights?(c(n),n.isGroup&&n.children.forEach(c)):r.push(a);let u;switch(O[i.path]){case O.weights:u=ie;break;case O.rotation:u=re;break;case O.translation:case O.scale:u=se;break;default:switch(s.itemSize){case 1:u=ie;break;case 2:case 3:default:u=se;break}break}const l=t.interpolation!==void 0?Fn[t.interpolation]:_e,h=this._getArrayFromAccessor(s);for(let d=0,p=r.length;d<p;d++){const T=new u(r[d]+"."+O[i.path],e.array,h,l);t.interpolation==="CUBICSPLINE"&&this._createCubicSplineTrackInterpolant(T),o.push(T)}return o}_getArrayFromAccessor(n){let e=n.array;if(n.normalized){const s=ee(e.constructor),t=new Float32Array(e.length);for(let i=0,o=e.length;i<o;i++)t[i]=e[i]*s;e=t}return e}_createCubicSplineTrackInterpolant(n){n.createInterpolant=function(s){const t=this instanceof re?kn:Se;return new t(this.times,this.values,this.getValueSize()/3,s)},n.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline=!0}}function Kn(f,n,e){const s=n.attributes,t=new an;if(s.POSITION!==void 0){const a=e.json.accessors[s.POSITION],r=a.min,c=a.max;if(r!==void 0&&c!==void 0){if(t.set(new k(r[0],r[1],r[2]),new k(c[0],c[1],c[2])),a.normalized){const u=ee(H[a.componentType]);t.min.multiplyScalar(u),t.max.multiplyScalar(u)}}else{console.warn("THREE.GLTFLoader: Missing min/max properties for accessor POSITION.");return}}else return;const i=n.targets;if(i!==void 0){const a=new k,r=new k;for(let c=0,u=i.length;c<u;c++){const l=i[c];if(l.POSITION!==void 0){const h=e.json.accessors[l.POSITION],d=h.min,p=h.max;if(d!==void 0&&p!==void 0){if(r.setX(Math.max(Math.abs(d[0]),Math.abs(p[0]))),r.setY(Math.max(Math.abs(d[1]),Math.abs(p[1]))),r.setZ(Math.max(Math.abs(d[2]),Math.abs(p[2]))),h.normalized){const T=ee(H[h.componentType]);r.multiplyScalar(T)}a.max(r)}else console.warn("THREE.GLTFLoader: Missing min/max properties for accessor POSITION.")}}t.expandByVector(a)}f.boundingBox=t;const o=new cn;t.getCenter(o.center),o.radius=t.min.distanceTo(t.max)/2,f.boundingSphere=o}function he(f,n,e){const s=n.attributes,t=[];function i(o,a){return e.getDependency("accessor",o).then(function(r){f.setAttribute(a,r)})}for(const o in s){const a=$[o]||o.toLowerCase();a in f.attributes||t.push(i(s[o],a))}if(n.indices!==void 0&&!f.index){const o=e.getDependency("accessor",n.indices).then(function(a){f.setIndex(a)});t.push(o)}return oe.workingColorSpace!==N&&"COLOR_0"in s&&console.warn(`THREE.GLTFLoader: Converting vertex colors from "srgb-linear" to "${oe.workingColorSpace}" not supported.`),b(f,n),Kn(f,n,e),Promise.all(t).then(function(){return n.targets!==void 0?Hn(f,n.targets,e):f})}export{Vn as GLTFLoader};
@@ -0,0 +1 @@
1
+ import{r,j as e}from"./vendor-react-Kv_JATwr.js";import{z as b,X as R,i as T,aT as W,f as B}from"./vendor-icons-CXrZ3R2V.js";import"./vendor-utils-aM7aYvPP.js";function A({isOpen:x,onClose:l,lang:s,onSeedSuccess:j,onJumpTab:N}){const[n,h]=r.useState(0),[v,w]=r.useState(!1),[F,y]=r.useState(null),[I,S]=r.useState(null),[k,p]=r.useState({display:"none"}),[C,m]=r.useState({display:"none"}),L=r.useRef(null),d=[{targetId:"",title:s==="zh"?"欢迎来到 OpenLearn Next V2 平台!":"Welcome to OpenLearn Next V2!",content:s==="zh"?"本平台为数字课堂提供智能白板与教学插件生态托管。下面我们将通过 4 步带您快速完成初始化并体验一堂数字互动课程。":"Welcome! This platform provides interactive whiteboards and educational plugin hosting. Let's set up your first class in 4 quick steps."},{targetId:"nav_btn_admin_directory",title:s==="zh"?"第 1 步:一键初始化示例课程":"Step 1: Initialize Example Class",content:s==="zh"?"在开始授课前,我们需要一个授课班级。请点击下方的按钮,系统将自动在数据库中为您建立示例班级、添加 5 名演示学生(小明、小红等),并自动排定一节今日的白板创意编程示范课!":"Before teaching, we need a classroom session. Click the button below to seed a demo class, add 5 sample students (Xiao Ming, Xiao Hong, etc.), and schedule today's creative programming class!",actionable:!0},{targetId:"navigation_sidebar",title:s==="zh"?"第 2 步:了解系统导航菜单":"Step 2: Tour System Navigation",content:s==="zh"?"初始化成功后,您可以在「系统导航」的「班级管理」中查看学生名册及座位表,或在「课程管理」中编辑白板教案。在「插件中心」中,您还可以安装/停用丰富的第三方小工具。":'After setup, explore the sidebar! Use "Classes & Students" to manage student profiles, "Courses" to edit whiteboards, and "Plugins" to manage tools.'},{targetId:"nav_btn_live_class",title:s==="zh"?"第 3 步:进入互动课堂开启体验":"Step 3: Launch Live Classroom",content:s==="zh"?"一切就绪!点击下方的「一键开课并进入课堂」,系统将为您自动进入示范课堂并展开互动白板。您可以在白板上下发测验、进行实时统计,学生端也将实时接收广播。":'All set! Click "Launch Classroom" to enter the live room. You can draw on the whiteboard, push quizzes, and view live stats, while student clients auto-sync.'}],E=async()=>{w(!0);try{const i=await fetch("/api/admin/seed-demo",{method:"POST"});if(i.ok){const t=await i.json();t.success&&(S({classId:t.classId,className:"人工智能与创意编程示范班"}),j({classId:t.classId,scheduleId:t.scheduleId,lessonId:t.lessonId}),setTimeout(()=>{h(2)},1500))}else{const t=await i.json().catch(()=>({error:"未知错误"}));y(s==="zh"?`初始化失败:${t.error||"请检查服务器日志"}`:`Failed: ${t.error||"Check server logs"}`)}}catch(i){console.error("Failed to seed demo data:",i),y(s==="zh"?`网络错误:${i.message||"请检查服务器是否运行"}`:`Network error: ${i.message||"Check if server is running"}`)}finally{w(!1)}},_=()=>{localStorage.setItem("edu_os_tour_completed","true"),N("live_class"),l()};if(r.useEffect(()=>{if(!x)return;const i=d[n];if(!i||!i.targetId){p({display:"none"}),m({position:"fixed",top:"50%",left:"50%",transform:"translate(-50%, -50%)",zIndex:1e4,width:"420px",maxWidth:"90vw"});return}const t=()=>{const z=document.getElementById(i.targetId);if(!z){p({display:"none"}),m({position:"fixed",top:"50%",left:"50%",transform:"translate(-50%, -50%)",zIndex:1e4,width:"420px",maxWidth:"90vw"});return}const o=z.getBoundingClientRect(),c=6;p({display:"block",position:"fixed",top:`${o.top-c}px`,left:`${o.left-c}px`,width:`${o.width+c*2}px`,height:`${o.height+c*2}px`,boxShadow:"0 0 0 9999px rgba(15, 23, 42, 0.65)",border:"2px dashed #6366f1",borderRadius:"12px",zIndex:9999,pointerEvents:"none",transition:"all 0.25s cubic-bezier(0.4, 0, 0.2, 1)"});const u=360;let f=o.left+o.width+16,g=o.top;f+u>window.innerWidth&&(f=Math.max(16,o.left-u-16)),g+240>window.innerHeight&&(g=Math.max(16,window.innerHeight-250)),m({display:"block",position:"fixed",top:`${g}px`,left:`${f}px`,width:`${u}px`,zIndex:1e4,transition:"all 0.25s cubic-bezier(0.4, 0, 0.2, 1)"})};t(),window.addEventListener("resize",t),window.addEventListener("scroll",t,!0);const $=setInterval(t,300);return()=>{window.removeEventListener("resize",t),window.removeEventListener("scroll",t,!0),clearInterval($)}},[x,n]),!x)return null;const a=d[n];return e.jsxs("div",{className:"fixed inset-0 overflow-hidden select-none",style:{zIndex:9998},children:[a&&!a.targetId&&e.jsx("div",{className:"fixed inset-0 bg-slate-900/65 backdrop-blur-2xs transition-opacity",onClick:l}),e.jsx("div",{style:k}),e.jsxs("div",{ref:L,style:C,className:"bg-white rounded-2xl border border-slate-150/60 shadow-2xl p-5 md:p-6 text-gray-900 font-sans flex flex-col justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex justify-between items-start mb-3",children:[e.jsxs("h3",{className:"font-extrabold text-sm md:text-base text-gray-900 flex items-center gap-1.5 leading-tight",children:[e.jsx(b,{size:16,className:"text-indigo-500 animate-pulse shrink-0"}),a.title]}),e.jsx("button",{onClick:l,className:"text-gray-400 hover:text-gray-600 rounded-lg p-0.5 transition-colors cursor-pointer",children:e.jsx(R,{size:16})})]}),e.jsx("p",{className:"text-xs md:text-sm text-gray-600 leading-relaxed font-medium mb-5 whitespace-pre-wrap",children:a.content}),a.actionable&&e.jsx("div",{className:"bg-slate-50 border border-slate-200/80 rounded-xl p-3.5 mb-5 flex flex-col items-center justify-center",children:I?e.jsxs("div",{className:"flex items-center gap-2 text-emerald-600 text-xs font-bold animate-bounce",children:[e.jsx(T,{size:12,className:"fill-emerald-600"}),s==="zh"?"示范课程与学生名册初始化成功!":"Demo classes successfully initialized!"]}):e.jsx("button",{onClick:E,disabled:v,className:"w-full flex items-center justify-center gap-1.5 py-2.5 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-bold text-xs md:text-sm rounded-lg hover:shadow-md transition-all cursor-pointer disabled:bg-indigo-400",children:v?e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsxs("svg",{className:"animate-spin h-4 w-4 text-white",fill:"none",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),s==="zh"?"正在创建数据库记录...":"Initializing DB records..."]}):e.jsxs(e.Fragment,{children:[e.jsx(b,{size:14,className:"animate-pulse"}),s==="zh"?"一键初始化示范数据":"Initialize Example Data"]})})})]}),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:d.map((i,t)=>e.jsx("div",{className:`h-1.5 rounded-full transition-all duration-300 ${t===n?"w-4 bg-indigo-600":"w-1.5 bg-slate-200"}`},t))}),e.jsxs("div",{className:"flex items-center gap-2",children:[n>0?e.jsxs("button",{onClick:()=>h(i=>i-1),className:"flex items-center gap-1 px-3 py-1.5 border border-slate-200 rounded-lg text-xs font-semibold text-slate-600 hover:bg-slate-50 transition-colors cursor-pointer",children:[e.jsx(W,{size:12}),s==="zh"?"上一步":"Back"]}):e.jsx("button",{onClick:l,className:"px-3 py-1.5 border border-slate-200 rounded-lg text-xs font-semibold text-slate-500 hover:bg-slate-50 transition-colors cursor-pointer",children:s==="zh"?"跳过向导":"Skip Tour"}),n<d.length-1?e.jsxs("button",{onClick:()=>h(i=>i+1),className:"flex items-center gap-1 px-3 py-1.5 bg-slate-900 text-white rounded-lg text-xs font-semibold hover:bg-slate-800 transition-colors cursor-pointer",children:[s==="zh"?"下一步":"Next",e.jsx(B,{size:12})]}):e.jsxs("button",{onClick:_,className:"flex items-center gap-1 px-4 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-xs font-bold shadow-sm transition-colors cursor-pointer",children:[s==="zh"?"结束引导并开课":"Launch Classroom",e.jsx(b,{size:12})]})]})]})]})]})}export{A as HelpTour};
@@ -0,0 +1,204 @@
1
+ import{r as m,j as e}from"./vendor-react-Kv_JATwr.js";import{M as $,E as W,c as U,e as H}from"./index-2676X8YB.js";import{as as N,ba as J,B as C,U as P,aY as L,ar as B,P as S,b as q,f as G,q as F,c as D,aD as M,o as Q,X as V,bW as _,a9 as E,w as v,am as Y,z as K,aQ as X,ao as Z,e as ee}from"./vendor-icons-CXrZ3R2V.js";import"./vendor-utils-aM7aYvPP.js";import"./vendor-pdf-C1Vh2Aqq.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-SrpBsvM3.js";const O=t=>t.startsWith("vfs.")?"vfs":t.startsWith("lesson.")||t.startsWith("whiteboard.")?"edu":t.startsWith("class.")||t.startsWith("student.")||t.startsWith("assignment.")||t.startsWith("attendance.")||t.startsWith("schedule.")?"mgmt":t.startsWith("process.")?"proc":t.startsWith("ai.")?"ai":"plugin",se=t=>{if(!t||t.type!=="OBJECT")return"{}";const n={};return t.properties&&Object.keys(t.properties).forEach(r=>{const d=t.properties[r];d.type==="ARRAY"?n[r]=d.items?.type==="STRING"?["选项 A","选项 B","选项 C"]:[]:d.type==="INTEGER"||d.type==="NUMBER"?n[r]=100:d.type==="BOOLEAN"?n[r]=!0:r.toLowerCase().includes("id")?n[r]="auto-id-or-current":r.toLowerCase().includes("name")?n[r]="测试名称":r.toLowerCase().includes("content")?n[r]=`# 初始内容
2
+ 这是一个通过命令创建的组件或段落。`:n[r]=d.description||"示例参数"}),JSON.stringify(n,null,2)},te=({registeredCommands:t,onRefresh:n})=>{const[r,d]=m.useState(""),[l,g]=m.useState("all"),[h,w]=m.useState(null),[y,f]=m.useState({}),[A,b]=m.useState({}),k=[{id:"all",name:"全部命令",icon:J},{id:"edu",name:"教学与画板",icon:C},{id:"mgmt",name:"班级与学生",icon:P},{id:"vfs",name:"虚拟文件系统",icon:L},{id:"proc",name:"进程控制",icon:N},{id:"ai",name:"AI 规划生成",icon:B},{id:"plugin",name:"第三方插件",icon:S}],I=m.useMemo(()=>{const s={};for(const a of t)s[a.id]=se(a.inputSchema);return s},[t]),T=s=>y[s]??I[s]??"{}",z=t.filter(s=>{const a=l==="all"||O(s.commandType)===l,i=r===""||s.commandType.toLowerCase().includes(r.toLowerCase())||s.description&&s.description.toLowerCase().includes(r.toLowerCase())||s.id.toLowerCase().includes(r.toLowerCase());return a&&i}),c=async(s,a)=>{b(i=>({...i,[a]:{success:!1,loading:!0}}));try{const i=T(a);let p={};try{p=JSON.parse(i)}catch(x){b(u=>({...u,[a]:{success:!1,error:`JSON 语法解析错误: ${x.message}`,loading:!1}}));return}const o=await fetch("/api/commands/execute",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({commandType:s,payload:p})}),j=await o.json();o.ok&&j.success?(b(x=>({...x,[a]:{success:!0,data:j.result,loading:!1}})),n()):b(x=>({...x,[a]:{success:!1,error:j.error||"执行命令失败",loading:!1}}))}catch(i){b(p=>({...p,[a]:{success:!1,error:i.message,loading:!1}}))}};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"p-4 border-b border-gray-100 bg-gray-50/50 flex flex-col gap-4 shrink-0 col-span-1",children:[e.jsxs("div",{className:"relative",children:[e.jsx(N,{className:"absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-400",size:16}),e.jsx("input",{type:"text",placeholder:"通过命令类型、描述或 Action ID 搜索活跃指令...",value:r,onChange:s=>d(s.target.value),className:"w-full pl-10 pr-4 py-2.5 bg-white border border-gray-200 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all shadow-sm"})]}),e.jsx("div",{className:"flex flex-wrap gap-2",children:k.map(s=>{const a=s.icon,i=l===s.id;return e.jsxs("button",{onClick:()=>g(s.id),className:`flex items-center gap-1.5 px-3.5 py-1.5 rounded-full text-xs font-semibold transition-all ${i?"bg-indigo-600 text-white shadow shadow-indigo-200 scale-102 font-bold":"bg-white text-gray-600 hover:text-gray-900 border border-gray-200 hover:border-gray-300"}`,children:[e.jsx(a,{size:13}),e.jsx("span",{children:s.name}),s.id==="all"?e.jsx("span",{className:`ml-1 text-xs px-1.5 py-0.2 rounded-full ${i?"bg-indigo-700/80 text-white":"bg-gray-100 text-gray-500"}`,children:t.length}):null]},s.id)})})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-gray-50/30",children:z.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center p-12 text-gray-400",children:[e.jsx(q,{size:48,className:"text-gray-300 mb-4 animate-pulse"}),e.jsx("h3",{className:"font-semibold text-gray-700",children:"没有找到匹配的指令"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:"请尝试清空查询条件或检查当前的插件状态"})]}):e.jsx("div",{"data-testid":"command-grid",role:"list",className:"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 max-w-[1600px] mx-auto",children:z.map(s=>{const a=h===s.id,i=O(s.commandType),p=s.isHighRisk,o=A[s.id],j=`command-panel-${s.id}`;return e.jsxs("div",{role:"listitem","data-testid":`command-card-${s.id}`,"data-expanded":a?"true":"false",className:`flex flex-col bg-white rounded-xl border transition-all duration-200 overflow-hidden shadow-sm hover:shadow ${a?"col-span-full":""} ${a?"border-indigo-400 ring-1 ring-indigo-100":p?"border-orange-200 hover:border-orange-300":"border-gray-200 hover:border-indigo-200"}`,children:[e.jsxs("button",{type:"button",onClick:()=>{w(a?null:s.id)},"aria-expanded":a,"aria-controls":j,className:`w-full flex-1 text-left p-4 flex flex-col gap-2.5 cursor-pointer hover:bg-gray-50/30 transition-colors select-none focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-inset rounded-xl ${a?"bg-indigo-50/40":""}`,children:[e.jsxs("div",{className:"flex items-start gap-2.5 min-w-0",children:[e.jsx("div",{className:`p-2 rounded-lg shrink-0 ${p?"bg-orange-50 text-orange-600":"bg-indigo-50 text-indigo-600"}`,children:i==="vfs"?e.jsx(L,{size:18}):i==="edu"?e.jsx(C,{size:18}):i==="mgmt"?e.jsx(P,{size:18}):i==="proc"?e.jsx(N,{size:18}):i==="ai"?e.jsx(B,{size:18}):e.jsx(S,{size:18})}),e.jsx("span",{className:"font-mono font-bold text-sm bg-gray-100 text-gray-800 px-2 py-0.5 rounded border border-gray-200 min-w-0 break-all",children:s.commandType})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[p&&e.jsx("span",{className:"text-xs bg-red-100 text-red-700 border border-red-200 rounded font-extrabold px-1.5 py-0.5 uppercase tracking-wide flex items-center gap-0.5",children:"⚠️ 高风险操作"}),e.jsxs("span",{className:"text-xs bg-indigo-50 text-indigo-700 border border-indigo-100 rounded-full px-2 py-0.5 max-w-full truncate",children:["🔒 ",s.capabilityRequired||"无公开权限"]})]}),e.jsx("p",{className:"text-gray-600 text-xs font-medium line-clamp-2 min-h-[2rem]",children:s.description||"(该命令没有描述)"}),e.jsxs("div",{className:"mt-auto flex items-center justify-between gap-2 pt-1",children:[e.jsxs("span",{className:"text-xs text-subtle font-mono truncate min-w-0",title:`ID: ${s.id}`,children:["ID: ",s.id]}),e.jsxs("span",{className:`text-xs px-3 py-1 rounded-lg font-semibold border transition-all flex items-center gap-1 shrink-0 ${a?"bg-indigo-50 border-indigo-200 text-indigo-700":"bg-gray-100 text-gray-700 border-gray-200"}`,children:[a?"折叠面板":"交互调试 Shell",e.jsx(G,{size:12,className:`transform transition-transform ${a?"rotate-90":""}`})]})]})]}),a&&e.jsxs("div",{id:j,className:"border-t border-gray-100 bg-gray-50/50 p-5 space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"bg-white rounded-lg border border-gray-200 p-4",children:[e.jsxs("h4",{className:"text-xs font-bold text-gray-700 uppercase tracking-widest mb-3 flex items-center gap-1.5 border-b border-gray-100 pb-2",children:[e.jsx(F,{size:12,className:"text-gray-400"}),"入参规范 (JSON Schema Definition)"]}),s.inputSchema?.properties?e.jsx("div",{className:"space-y-3 font-mono text-xs",children:Object.keys(s.inputSchema.properties).map(x=>{const u=s.inputSchema.properties[x],R=s.inputSchema.required?.includes(x);return e.jsxs("div",{className:"flex flex-col gap-1 border-b border-gray-50 last:border-b-0 pb-1.5",children:[e.jsxs("div",{className:"flex items-baseline gap-1.5",children:[e.jsx("span",{className:"text-indigo-600 font-semibold",children:x}),e.jsxs("span",{className:"text-subtle text-xs",children:["(",u.type,")"]}),R&&e.jsx("span",{className:"text-red-700 text-xs font-bold bg-red-50 border border-red-200 rounded px-1",children:"REQUIRED"})]}),u.description&&e.jsx("span",{className:"text-gray-500 font-sans leading-relaxed text-xs",children:u.description})]},x)})}):e.jsx("p",{className:"text-xs text-gray-400 italic",children:"该命令不需要接收任何入参负载 (Payload)。"})]}),e.jsxs("div",{className:"bg-white rounded-lg border border-gray-200 p-4 flex flex-col",children:[e.jsxs("h4",{className:"text-xs font-bold text-gray-700 uppercase tracking-widest mb-3 flex items-center gap-1.5 border-b border-gray-100 pb-2 justify-between",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx(N,{size:12,className:"text-gray-400"}),"Payload 调试区"]}),e.jsx("button",{onClick:()=>{f(x=>({...x,[s.id]:I[s.id]??"{}"}))},className:"text-xs text-indigo-600 hover:underline hover:text-indigo-800 uppercase tracking-wider",children:"恢复默认模版"})]}),e.jsx("label",{htmlFor:`command-payload-${s.id}`,className:"text-xs font-semibold text-subtle block mb-1",children:"Payload JSON:"}),e.jsx("textarea",{id:`command-payload-${s.id}`,value:T(s.id),onChange:x=>{const u=x.target.value;f(R=>({...R,[s.id]:u}))},rows:6,className:"w-full font-mono text-xs p-2.5 bg-gray-900 text-indigo-300 border border-gray-800 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:outline-none resize-none leading-relaxed flex-1 shadow-inner"}),e.jsx("div",{className:"mt-3 flex justify-end",children:e.jsx("button",{onClick:()=>c(s.commandType,s.id),disabled:o?.loading,className:`px-4 py-2 text-xs font-bold font-sans text-white hover:shadow-md active:scale-95 transition-all flex items-center gap-1.5 rounded-lg ${p?"bg-red-600 hover:bg-red-700":"bg-indigo-600 hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500"} disabled:opacity-50`,children:o?.loading?e.jsxs(e.Fragment,{children:[e.jsx(D,{size:13,className:"animate-spin"}),e.jsx("span",{children:"内核正在调度总线..."})]}):e.jsxs(e.Fragment,{children:[e.jsx(M,{size:13}),e.jsx("span",{children:"提交执行指令 (Deploy Command)"})]})})})]})]}),o&&e.jsxs("div",{className:`p-4 rounded-xl border flex flex-col font-mono text-xs leading-relaxed relative ${o.loading?"bg-gray-50 border-gray-200":o.success?"bg-green-50/50 border-green-200 text-green-900":"bg-red-50/50 border-red-200 text-red-900"}`,children:[e.jsx("div",{className:"absolute top-2 right-3 uppercase text-xs font-bold text-gray-400",children:"Console Output log"}),e.jsx("div",{className:"font-bold flex items-center gap-1.5 mb-1 bg-transparent border-0 p-0 text-xs text-neutral-800",children:o.loading?e.jsx("span",{className:"text-gray-500",children:"⏳ COMMAND QUEUED..."}):o.success?e.jsxs("span",{className:"text-green-700 flex items-center gap-1",children:[e.jsx(Q,{size:14})," STATUS: 200 SUCCESS (Action Completed)"]}):e.jsxs("span",{className:"text-red-700 flex items-center gap-1",children:[e.jsx(V,{size:14})," STATUS: 500 INTERNAL_BUS_ERROR"]})}),!o.loading&&e.jsx("pre",{className:"mt-2 p-3 bg-gray-900/95 text-gray-200 rounded-lg overflow-x-auto border border-gray-800 shadow-inner max-h-56 select-all font-mono",children:o.success?JSON.stringify(o.data,null,2):o.error})]})]})]},s.id)})})})]})},re=({pluginGuideMd:t,loadingMd:n,copiedId:r,handleCopy:d,pluginBoilerplateCode:l,pluginInteractiveCode:g,pluginExamCode:h})=>e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-slate-50/50",children:e.jsxs("div",{className:"max-w-5xl mx-auto space-y-6",children:[n?e.jsxs("div",{className:"flex flex-col items-center justify-center p-12 bg-white rounded-2xl border border-gray-200",children:[e.jsx(D,{size:32,className:"animate-spin text-indigo-600 mb-2"}),e.jsx("span",{className:"text-xs text-gray-500 font-semibold",children:"正在从服务器拉取 SDK 文档..."})]}):t?e.jsx("div",{className:"bg-white rounded-2xl border border-gray-200 p-8 shadow-sm prose prose-slate max-w-none text-xs leading-relaxed",children:e.jsx($,{children:t})}):null,e.jsxs("div",{className:"bg-white rounded-2xl border border-indigo-100 p-6 shadow-sm flex flex-col md:flex-row gap-5 items-start",children:[e.jsx("div",{className:"p-4 bg-indigo-50 text-indigo-600 rounded-2xl shrink-0",children:e.jsx(S,{size:28})}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("h3",{className:"text-base font-bold text-gray-900",children:"Openlearn Next 插件开发指南 & API 参考"}),e.jsxs("p",{className:"text-xs text-gray-600 leading-relaxed",children:["Openlearn Next 基于 ",e.jsx("span",{className:"font-semibold text-indigo-600",children:"CommandBus(命令总线)"})," +"," ",e.jsx("span",{className:"font-semibold text-indigo-600",children:"EventBus(事件总线)"})," 微内核架构。 插件通过标准 ESM 模块导出 ",e.jsx("code",{className:"bg-gray-100 text-rose-600 px-1 rounded text-xs",children:"activate(ctx)"})," 函数接收"," ",e.jsx("span",{className:"font-semibold",children:"PluginContext"}),", 进而访问 7 大内核服务。本页提供完整的 API 参考、参数说明和可运行示例。"]}),e.jsxs("div",{className:"flex flex-wrap gap-2 pt-1",children:[e.jsx("span",{className:"text-xs bg-indigo-50 text-indigo-700 border border-indigo-100 px-2 py-0.5 rounded-full font-medium",children:"🔄 DI 依赖注入"}),e.jsx("span",{className:"text-xs bg-emerald-50 text-emerald-700 border border-emerald-100 px-2 py-0.5 rounded-full font-medium",children:"🔒 能力安全模型"}),e.jsx("span",{className:"text-xs bg-amber-50 text-amber-700 border border-amber-100 px-2 py-0.5 rounded-full font-medium",children:"⚡ 热重载 + Worker 隔离"}),e.jsx("span",{className:"text-xs bg-rose-50 text-rose-700 border border-rose-100 px-2 py-0.5 rounded-full font-medium",children:"📦 ESM + CommonJS 双格式"}),e.jsx("span",{className:"text-xs bg-sky-50 text-sky-700 border border-sky-100 px-2 py-0.5 rounded-full font-medium",children:"🆔 别名生命周期 (Alias Lifecycle)"}),e.jsx("span",{className:"text-xs bg-teal-50 text-teal-700 border border-teal-100 px-2 py-0.5 rounded-full font-medium",children:"🛡️ Worker Storage 安全隔离"})]})]})]}),e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 overflow-hidden shadow-sm",children:[e.jsxs("div",{className:"px-5 py-4 bg-gray-50 border-b border-gray-100 flex flex-col sm:flex-row sm:items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("div",{className:"p-1.5 bg-indigo-50 text-indigo-600 rounded",children:e.jsx(_,{size:16})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-gray-900",children:"示例 1:思维导图插件 — 注册 Action + 处理器 + 发布事件"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"演示 ctx.services 解构、Action 注册、createCommand 创建信封、eventBus.publish 发布事件"})]})]}),e.jsxs("button",{onClick:()=>d("tpl1",l),className:`px-3 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all shadow-sm shrink-0 ${r==="tpl1"?"bg-emerald-50 text-emerald-700 border border-emerald-250":"bg-white text-indigo-600 border border-indigo-150 hover:bg-indigo-50"}`,children:[r==="tpl1"?e.jsx(E,{size:12}):e.jsx(v,{size:12}),e.jsx("span",{children:r==="tpl1"?"已复制!":"复制代码"})]})]}),e.jsx("pre",{className:"text-xs font-mono p-5 bg-gray-950 text-gray-200 overflow-x-auto leading-relaxed max-h-[360px] shadow-inner select-all",children:l})]}),e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 overflow-hidden shadow-sm",children:[e.jsxs("div",{className:"px-5 py-4 bg-gray-50 border-b border-gray-100 flex flex-col sm:flex-row sm:items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("div",{className:"p-1.5 bg-emerald-50 text-emerald-600 rounded",children:e.jsx(_,{size:16})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-gray-900",children:"示例 2:AI 作业批改插件 — 演示 AI + 事件订阅 + 存储 + DI"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"演示 ctx.resolve 获取数据库、services.ai 生成文本、services.storage 持久化、eventBus 订阅"})]})]}),e.jsxs("button",{onClick:()=>d("tpl2",g),className:`px-3 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all shadow-sm shrink-0 ${r==="tpl2"?"bg-emerald-50 text-emerald-700 border border-emerald-250":"bg-white text-emerald-600 border border-emerald-150 hover:bg-emerald-50"}`,children:[r==="tpl2"?e.jsx(E,{size:12}):e.jsx(v,{size:12}),e.jsx("span",{children:r==="tpl2"?"已复制!":"复制代码"})]})]}),e.jsx("pre",{className:"text-xs font-mono p-5 bg-gray-950 text-gray-200 overflow-x-auto leading-relaxed max-h-[360px] shadow-inner select-all",children:g})]}),e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 overflow-hidden shadow-sm",children:[e.jsxs("div",{className:"px-5 py-4 bg-gray-50 border-b border-gray-100 flex flex-col sm:flex-row sm:items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("div",{className:"p-1.5 bg-amber-50 text-amber-600 rounded",children:e.jsx(_,{size:16})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-gray-900",children:"示例 3:考试系统插件 — 演示 ctx.db 自建表 + deactivate 清理"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"演示 ctx.db.ensureTable 建表、ctx.db.table 获取带前缀表名、deactivate 生命周期"})]})]}),e.jsxs("button",{onClick:()=>d("tpl3",h),className:`px-3 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all shadow-sm shrink-0 ${r==="tpl3"?"bg-emerald-50 text-emerald-700 border border-emerald-250":"bg-white text-amber-600 border border-amber-150 hover:bg-amber-50"}`,children:[r==="tpl3"?e.jsx(E,{size:12}):e.jsx(v,{size:12}),e.jsx("span",{children:r==="tpl3"?"已复制!":"复制代码"})]})]}),e.jsx("pre",{className:"text-xs font-mono p-5 bg-gray-950 text-gray-200 overflow-x-auto leading-relaxed max-h-[360px] shadow-inner select-all",children:h})]})]})}),ae=({copiedId:t,handleCopy:n})=>e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-slate-50/50",children:e.jsxs("div",{className:"max-w-5xl mx-auto space-y-8",children:[e.jsxs("div",{className:"bg-gradient-to-r from-indigo-900 to-indigo-950 rounded-2xl p-6 text-white shadow-md flex flex-col md:flex-row gap-5 items-start",children:[e.jsx("div",{className:"p-4 bg-indigo-500/20 text-indigo-300 border border-indigo-500/30 rounded-2xl shrink-0",children:e.jsx(C,{size:32})}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("h3",{className:"text-lg font-bold text-white flex items-center gap-2",children:["Openlearn Next 核心系统主要特性使用教程",e.jsx("span",{className:"text-xs bg-indigo-500/30 text-indigo-200 border border-indigo-500/50 px-2 py-0.5 rounded-full font-normal",children:"v2.1.0 LTS"})]}),e.jsx("p",{className:"text-xs text-indigo-200 leading-relaxed",children:"本指南为教育实验操作系统 (Openlearn Next) 的深度使用手册。详细阐述如何使用内核指令管理班级与学生、基于大纲结构化推进课件时间轴、在互动白板中动态注入脑图和测验元素、启动全屏锁定与签到的教学控制流,以及编写并集成支持 AI 成绩回传的 HTML Applet 实验组件。"}),e.jsxs("div",{className:"flex flex-wrap gap-2 pt-1",children:[e.jsx("span",{className:"text-xs bg-indigo-800/40 text-indigo-150 border border-indigo-700/50 px-2 py-0.5 rounded-full font-medium",children:"🏫 班级与学生指令集"}),e.jsx("span",{className:"text-xs bg-emerald-800/40 text-emerald-150 border border-emerald-700/50 px-2 py-0.5 rounded-full font-medium",children:"📋 课程大纲时间轴同步"}),e.jsx("span",{className:"text-xs bg-amber-800/40 text-amber-150 border border-amber-700/50 px-2 py-0.5 rounded-full font-medium",children:"🎨 协作白板指令渲染"}),e.jsx("span",{className:"text-xs bg-sky-800/40 text-sky-150 border border-sky-700/50 px-2 py-0.5 rounded-full font-medium",children:"🚀 postMessage 成绩监听"})]})]})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6",children:[e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 shadow-sm p-6 space-y-4 hover:shadow-md transition-all flex flex-col justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-3 border-b border-gray-100 pb-3 text-indigo-600 font-bold",children:[e.jsx("div",{className:"p-2 bg-indigo-50 rounded-xl",children:e.jsx(P,{size:20})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm text-gray-900",children:"1. 使用核心指令管理班级与学生"}),e.jsx("p",{className:"text-xs text-gray-400 font-normal mt-0.5",children:"Administrative CLI & Capability Model"})]})]}),e.jsxs("p",{className:"text-xs text-gray-600 leading-relaxed mt-3",children:["Openlearn Next 采用权限隔离的",e.jsx("strong",{children:"能力安全模型 (Capability Model)"}),"。任何行政、学籍以及课表排期修改,底层最终都会被封装为非对称的内核指令,经由分布式指令总线进行安全校验与事务落库。"]}),e.jsxs("div",{className:"p-3 bg-indigo-50/50 rounded-xl border border-indigo-100 text-xs text-indigo-900 space-y-1.5 mt-3",children:[e.jsx("span",{className:"font-bold block",children:"🔒 核心访问权限声明 (RBAC Capabilities):"}),e.jsxs("ul",{className:"list-disc list-inside space-y-1 text-gray-600 text-xs",children:[e.jsxs("li",{children:[e.jsx("code",{className:"bg-white/80 px-1 rounded border border-indigo-150",children:"class:write"}),":允许创建/删除行政班级。"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-white/80 px-1 rounded border border-indigo-150",children:"student:write"}),":允许注册学生、修改学籍或修改设备锁定状态。"]}),e.jsxs("li",{children:[e.jsx("code",{className:"bg-white/80 px-1 rounded border border-indigo-150",children:"schedule:write"}),":允许编排课程,或临时将某一日期的课程替换为星期几的常规课表。"]})]})]})]}),e.jsxs("div",{className:"space-y-3 font-mono text-xs mt-4",children:[e.jsxs("div",{className:"bg-gray-50 rounded-xl p-3 border border-gray-150 relative",children:[e.jsxs("div",{className:"flex justify-between items-center mb-1",children:[e.jsx("span",{className:"text-indigo-600 font-bold",children:"A. 创建班级 (class.create)"}),e.jsx("button",{onClick:()=>n("guide_cmd_class",`{
3
+ "name": "高三一班",
4
+ "description": "物理实验班"
5
+ }`),className:"text-xs text-indigo-600 hover:underline hover:text-indigo-800 font-sans",children:t==="guide_cmd_class"?"已复制":"复制参数"})]}),e.jsx("pre",{className:"text-xs text-gray-500 overflow-x-auto whitespace-pre-wrap leading-relaxed",children:`{
6
+ "name": "高三一班",
7
+ "description": "2026届物理实验班"
8
+ }`})]}),e.jsxs("div",{className:"bg-gray-50 rounded-xl p-3 border border-gray-150 relative",children:[e.jsxs("div",{className:"flex justify-between items-center mb-1",children:[e.jsx("span",{className:"text-indigo-600 font-bold",children:"B. 注册学生 (student.register)"}),e.jsx("button",{onClick:()=>n("guide_cmd_student",`{
9
+ "name": "李华",
10
+ "email": "lihua@openlearn.org",
11
+ "password": "mypassword123"
12
+ }`),className:"text-xs text-indigo-600 hover:underline hover:text-indigo-800 font-sans",children:t==="guide_cmd_student"?"已复制":"复制参数"})]}),e.jsx("pre",{className:"text-xs text-gray-500 overflow-x-auto whitespace-pre-wrap leading-relaxed",children:`{
13
+ "name": "李华",
14
+ "email": "lihua@openlearn.org",
15
+ "password": "mypassword123"
16
+ }`})]}),e.jsxs("div",{className:"bg-gray-50 rounded-xl p-3 border border-gray-150 relative",children:[e.jsxs("div",{className:"flex justify-between items-center mb-1",children:[e.jsx("span",{className:"text-indigo-600 font-bold",children:"C. 强制学生屏幕锁定 (student.lock_lesson)"}),e.jsx("button",{onClick:()=>n("guide_cmd_lock",`{
17
+ "studentId": "student-uuid-xxxx",
18
+ "lockedLessonId": "lesson-uuid-yyyy"
19
+ }`),className:"text-xs text-indigo-600 hover:underline hover:text-indigo-800 font-sans",children:t==="guide_cmd_lock"?"已复制":"复制参数"})]}),e.jsx("pre",{className:"text-xs text-gray-500 overflow-x-auto whitespace-pre-wrap leading-relaxed",children:`{
20
+ "studentId": "student-uuid-xxxx",
21
+ "lockedLessonId": "lesson-uuid-yyyy"
22
+ }`})]})]})]}),e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 shadow-sm p-6 space-y-4 hover:shadow-md transition-all flex flex-col justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-3 border-b border-gray-100 pb-3 text-emerald-600 font-bold",children:[e.jsx("div",{className:"p-2 bg-emerald-50 rounded-xl",children:e.jsx(Y,{size:20})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm text-gray-900",children:"2. 课程编辑与时间轴推进"}),e.jsx("p",{className:"text-xs text-gray-400 font-normal mt-0.5",children:"Structure Markdown & Timeline Sync"})]})]}),e.jsxs("div",{className:"space-y-3 mt-3 text-xs text-gray-600 leading-relaxed",children:[e.jsx("p",{children:'在系统"课程管理"中,教师可以使用内置大纲结构化的 Markdown 工具编排课件,或选择数学、计算机科学、文学、物理、历史、艺术模板一键填充结构化实验教案。'}),e.jsxs("div",{className:"p-3 bg-gray-50 rounded-xl border border-gray-150 space-y-1 text-xs font-sans",children:[e.jsx("span",{className:"font-bold text-gray-800 block",children:"📝 大纲解析与进度切片规则:"}),e.jsxs("p",{className:"text-gray-500",children:["系统内核采用递归的 AST 语法分析器,提取文档中的一级标题 (",e.jsx("code",{className:"font-mono text-rose-600 font-semibold bg-gray-100 px-0.5 rounded",children:"#"}),") 与二级标题 (",e.jsx("code",{className:"font-mono text-rose-600 font-semibold bg-gray-100 px-0.5 rounded",children:"##"}),") 作为教学",e.jsx("b",{children:"时间轴切片 (Timeline Segments)"}),"。"]})]}),e.jsxs("div",{className:"p-3.5 bg-emerald-50/50 rounded-xl border border-emerald-100 space-y-2 text-xs text-emerald-800",children:[e.jsxs("span",{className:"font-bold flex items-center gap-1",children:[e.jsx(K,{size:12})," 时间轴实时推进机制 (Sync Protocol):"]}),e.jsxs("ol",{className:"list-decimal list-inside space-y-1 text-gray-600 leading-relaxed",children:[e.jsxs("li",{children:["当教师在教学控制台选中大纲某个段落时,前端会通过 WebSocket 通信管道发送一个"," ",e.jsx("code",{className:"font-mono bg-white text-emerald-700 px-1 rounded border border-emerald-150",children:"sync_timeline"})," ","广播包。"]}),e.jsxs("li",{children:["学生终端捕获该消息包,解析出对应的锚点 DOM ID,并自动执行 DOM 的"," ",e.jsxs("code",{className:"font-mono bg-white text-emerald-700 px-1 rounded border border-emerald-150",children:[".scrollIntoView(","{ behavior: 'smooth' }",")"]})," ","动画平滑定位。"]}),e.jsxs("li",{children:["在此模式下,学生终端的大脑侧边滚轮处于",e.jsx("b",{children:"阻尼锁定状态"}),",禁止自行向上游或下游浏览,以保证全体学生始终与教师保持完全一致的视野。"]})]})]})]})]}),e.jsxs("div",{className:"p-3 bg-gray-50 rounded-xl border border-gray-150 text-xs text-gray-500 leading-relaxed",children:["💡 ",e.jsx("span",{className:"font-semibold text-gray-700",children:"常规课表与临时调整:"}),'如果需要将某一天的课程临时调换为星期几的常规安排(如将下周一临时指定为星期五课表),可在"课表看板"或使用'," ",e.jsx("code",{className:"bg-gray-100 font-mono text-rose-600 px-1 rounded",children:"schedule.update_date_mapping"})," ","指令进行快速热重载,数据库将自动持久化这种临时的映射规则。"]})]}),e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 shadow-sm p-6 space-y-4 hover:shadow-md transition-all flex flex-col justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-3 border-b border-gray-100 pb-3 text-amber-600 font-bold",children:[e.jsx("div",{className:"p-2 bg-amber-50 rounded-xl",children:e.jsx(X,{size:20})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm text-gray-900",children:"3. 互动协作白板深度原理"}),e.jsx("p",{className:"text-xs text-gray-400 font-normal mt-0.5",children:"Whiteboard Drawing API & State Serialization"})]})]}),e.jsx("p",{className:"text-xs text-gray-600 leading-relaxed mt-3",children:"协作白板采用轻量级矢量序列化格式存储。每一笔涂鸦、矩形、圆形、文本或第三方卡片均被抽象为带有全局唯一 ID 的矢量节点模型,实时在 SQLite 数据库中持久化并在前端的 Canvas/SVG 容器中完成合并渲染。"}),e.jsxs("div",{className:"p-3 bg-gray-50 rounded-xl border border-gray-150 text-xs font-mono text-gray-500 space-y-1 mt-3",children:[e.jsx("span",{className:"font-bold text-gray-700 block font-sans",children:"💾 矢量节点序列化数据示例 (SQLite Schema):"}),e.jsx("pre",{className:"text-xs overflow-x-auto whitespace-pre-wrap",children:`{
23
+ "id": "elt-90928a",
24
+ "type": "rect",
25
+ "data": "{\\"x\\":120,\\"y\\":80,\\"w\\":150,\\"h\\":60,\\"stroke\\":\\"#6366f1\\",\\"fill\\":\\"#e0e7ff\\"}"
26
+ }`})]}),e.jsxs("div",{className:"p-3 bg-amber-50/50 rounded-xl border border-amber-100 text-xs text-amber-900 space-y-2 mt-3",children:[e.jsx("span",{className:"font-bold block",children:"💡 互动绘制指令说明 (whiteboard.draw):"}),e.jsxs("p",{className:"text-gray-600 leading-relaxed",children:["除了在画板上使用画笔绘图外,插件生态亦可通过向总线派发"," ",e.jsx("code",{className:"bg-white text-amber-700 border border-amber-150 px-1 rounded font-mono",children:"whiteboard.draw"})," ","指令,动态在指定位置绘制结构化的几何内容或思维导图组件。"]})]})]}),e.jsxs("div",{className:"bg-amber-50/50 rounded-xl p-3 border border-amber-100 text-xs text-amber-800 relative mt-4",children:[e.jsxs("div",{className:"flex justify-between items-center mb-1",children:[e.jsx("span",{className:"font-bold",children:"A. 动态绘制思维脑图参数示例"}),e.jsx("button",{onClick:()=>n("guide_cmd_draw_map",`{
27
+ "lessonId": "lesson-101",
28
+ "type": "mindmap",
29
+ "data": "{\\"title\\":\\"光电效应\\",\\"branches\\":[\\"赫兹发现\\",\\"爱因斯坦解释\\"]}"
30
+ }`),className:"text-xs text-amber-700 hover:underline font-sans font-bold",children:t==="guide_cmd_draw_map"?"已复制":"复制 Payload"})]}),e.jsx("pre",{className:"font-mono text-xs text-amber-900/70 overflow-x-auto",children:`{
31
+ "lessonId": "lesson-101",
32
+ "type": "mindmap",
33
+ "data": "{\\"title\\":\\"光电效应\\",\\"branches\\":[\\"赫兹发现\\",\\"爱因斯坦解释\\"]}"
34
+ }`})]})]}),e.jsxs("div",{className:"bg-white rounded-2xl border border-gray-200 shadow-sm p-6 space-y-4 hover:shadow-md transition-all flex flex-col justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-3 border-b border-gray-100 pb-3 text-sky-600 font-bold",children:[e.jsx("div",{className:"p-2 bg-sky-50 rounded-xl",children:e.jsx(M,{size:20})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm text-gray-900",children:"4. 现场教学与课堂控制流程"}),e.jsx("p",{className:"text-xs text-gray-400 font-normal mt-0.5",children:"Live Classroom Lifecycle & WS Protocol"})]})]}),e.jsx("p",{className:"text-xs text-gray-600 leading-relaxed mt-3",children:"现场教学是 Openlearn Next 实现教师端对全班学生端实时掌控、授课与互动的核心驱动模块。以下是系统推荐的标准课堂控制流:"}),e.jsxs("div",{className:"space-y-3 text-xs mt-3",children:[e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("div",{className:"h-5 w-5 font-mono font-bold bg-sky-100 border border-sky-200 text-sky-700 rounded-lg flex items-center justify-center shrink-0 mt-0.5",children:"1"}),e.jsxs("div",{children:[e.jsx("span",{className:"font-semibold text-gray-800",children:"课堂通道初始化 (WebSocket Connect)"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:'点击"开始上课"后,服务器将为该班级开辟独立的 Room 广播室。所有在该行政班级内的学生设备自动建立双向心跳长连接。'})]})]}),e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("div",{className:"h-5 w-5 font-mono font-bold bg-sky-100 border border-sky-200 text-sky-700 rounded-lg flex items-center justify-center shrink-0 mt-0.5",children:"2"}),e.jsxs("div",{children:[e.jsx("span",{className:"font-semibold text-gray-800",children:"全屏课件与屏幕强控 (Class Lock)"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"教师启动教学视图后,系统会广播 `class_lock` 事件。学生终端界面被迫最小化非教学区,全屏强制渲染指定的课时 Markdown 文档,拦截一切其他的键盘路由跳转。"})]})]}),e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("div",{className:"h-5 w-5 font-mono font-bold bg-sky-100 border border-sky-200 text-sky-700 rounded-lg flex items-center justify-center shrink-0 mt-0.5",children:"3"}),e.jsxs("div",{children:[e.jsx("span",{className:"font-semibold text-gray-800",children:"出勤签到统计 (Roll Call & Attendance)"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"教师一键下发签到令牌,学生端界面滑出覆盖式的签到组件。系统利用 WebSocket 增量计数,在教师端看板的考勤环形图中实时计算到课率、迟到率以及旷课名单。"})]})]}),e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("div",{className:"h-5 w-5 font-mono font-bold bg-sky-100 border border-sky-200 text-sky-700 rounded-lg flex items-center justify-center shrink-0 mt-0.5",children:"4"}),e.jsxs("div",{children:[e.jsx("span",{className:"font-semibold text-gray-800",children:"交互过程感知与动态审计 (Activity Auditing)"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"学生每一次提交的实验数据、成绩变动、签到时刻等操作,均以结构化日志格式推送并在教师端的动态操作审计终端上显示。"})]})]})]})]}),e.jsxs("div",{className:"p-3 bg-sky-50/50 rounded-xl border border-sky-100 text-xs text-sky-850 flex items-center gap-2 mt-4",children:[e.jsx("span",{className:"h-2 w-2 rounded-full bg-emerald-500 animate-ping"}),e.jsxs("span",{children:[e.jsx("strong",{children:"考勤数据存根:"}),"签到动作一经产生,将自动持久化至底层的"," ",e.jsx("code",{className:"font-mono text-xs bg-white px-1 border border-sky-200 rounded",children:"attendance"})," ","表中,随时供导出 PDF 或生成学期报告。"]})]})]})]}),e.jsxs("div",{className:"bg-gradient-to-br from-slate-900 to-slate-950 text-white rounded-2xl p-6 space-y-6 shadow-md",children:[e.jsxs("div",{className:"flex items-center gap-3 border-b border-slate-800 pb-3 text-indigo-400 font-bold",children:[e.jsx("div",{className:"p-2 bg-slate-800 rounded-xl",children:e.jsx(Z,{size:24})}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-base text-white",children:"5. HTML Applet 的打包、分发与 AI 成绩监听注入机制"}),e.jsx("p",{className:"text-xs text-slate-400 font-normal mt-0.5",children:"HTML Applet Architecture & parent.postMessage Interface Spec"})]})]}),e.jsx("div",{className:"text-xs text-slate-300 leading-relaxed space-y-3",children:e.jsxs("p",{children:["HTML Applet 是 Openlearn Next 系统架构中用以承载高级虚拟物理实验、交互式小游戏、或第三方考试系统的微网页插件。Applet 被封装运行在宿主页面的 ",e.jsx("code",{className:"bg-slate-800 text-indigo-300 px-1 rounded font-mono",children:"iframe"})," ","沙箱容器中。"]})})]})]})}),ne=()=>e.jsx("div",{className:"flex-1 overflow-y-auto p-6 bg-gray-50/30",children:e.jsx("div",{className:"max-w-5xl mx-auto space-y-6",children:e.jsxs("div",{className:"bg-white rounded-xl border border-gray-200 p-6 shadow-sm",children:[e.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[e.jsx("div",{className:"p-2 bg-indigo-50 rounded-lg text-indigo-600",children:e.jsx(v,{size:20})}),e.jsxs("div",{children:[e.jsx("h3",{className:"text-base font-bold text-gray-900",children:"已安装插件的使用文档"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:"插件开发者可在 manifest.contributes 中声明 help.plugin_docs 贡献项,或通过扩展点注册文档组件。"})]})]}),e.jsx(W,{slot:"help.plugin_docs"})]})})});function ge(t={}){const n=U(),r=t.registeredCommands??n?.registeredCommands??[],d=t.onRefresh??n?.fetchRegisteredCommands??(()=>{});H(c=>c.lang);const[l,g]=m.useState("commands"),[h,w]=m.useState(null),[y,f]=m.useState(""),[A,b]=m.useState(!1);m.useEffect(()=>{l==="sdk_guide"&&!y&&(b(!0),fetch("/api/docs/plugin-guide").then(c=>c.json()).then(c=>{c.success?f(c.content):f("加载失败: "+(c.error||"未知错误"))}).catch(c=>{f("加载出错: "+c.message)}).finally(()=>{b(!1)}))},[l,y]);const k=(c,s)=>{navigator.clipboard.writeText(s),w(c),setTimeout(()=>w(null),1500)};return e.jsxs("div",{className:"flex-1 flex flex-col h-full bg-white text-gray-900 border border-gray-200 rounded-2xl shadow-sm overflow-hidden m-1",children:[e.jsxs("div",{className:"px-6 py-5 bg-gradient-to-r from-indigo-50 via-purple-50 to-indigo-50/20 shrink-0 border-b border-gray-150 flex flex-col md:flex-row md:items-center justify-between gap-4",children:[e.jsxs("div",{children:[e.jsxs("h2",{className:"text-xl font-bold text-gray-900 flex items-center gap-2",children:[e.jsx(ee,{className:"text-indigo-600",size:24}),"教育实验操作系统:内核帮助与开发中心 (Openlearn Next Reference Hub)"]}),e.jsx("p",{className:"text-xs text-subtle mt-1",children:"本页提供 OpenLearn 插件开发的快速入口:脚手架工具、交互式代码范例、完整的 API 参考文档。"})]}),e.jsxs("div",{className:"flex bg-neutral-100 p-0.5 rounded-xl border border-neutral-200 self-start md:self-center shrink-0 shadow-inner",children:[e.jsxs("button",{onClick:()=>g("commands"),className:`px-4 py-1.5 rounded-lg text-xs font-semibold tracking-wide transition-all flex items-center gap-1.5 ${l==="commands"?"bg-white text-indigo-700 shadow-sm font-bold border border-neutral-200/50":"text-muted hover:text-gray-800"}`,children:[e.jsx(N,{size:14}),e.jsx("span",{children:"指令调试 (Command Playground)"})]}),e.jsxs("button",{onClick:()=>g("sdk_guide"),className:`px-4 py-1.5 rounded-lg text-xs font-semibold tracking-wide transition-all flex items-center gap-1.5 ${l==="sdk_guide"?"bg-white text-indigo-700 shadow-sm font-bold border border-neutral-200/50":"text-muted hover:text-gray-800"}`,children:[e.jsx(S,{size:14}),e.jsx("span",{children:"插件开发指南 & API"})]}),e.jsxs("button",{onClick:()=>g("user_guide"),className:`px-4 py-1.5 rounded-lg text-xs font-semibold tracking-wide transition-all flex items-center gap-1.5 ${l==="user_guide"?"bg-white text-indigo-700 shadow-sm font-bold border border-neutral-200/50":"text-muted hover:text-gray-800"}`,children:[e.jsx(C,{size:14}),e.jsx("span",{children:"系统使用教程"})]}),e.jsxs("button",{onClick:()=>g("plugin_docs"),className:`px-4 py-1.5 rounded-lg text-xs font-semibold tracking-wide transition-all flex items-center gap-1.5 ${l==="plugin_docs"?"bg-white text-indigo-700 shadow-sm font-bold border border-neutral-200/50":"text-muted hover:text-gray-800"}`,children:[e.jsx(v,{size:14}),e.jsx("span",{children:"扩展文档"})]})]})]}),l==="commands"&&e.jsx(te,{registeredCommands:r,onRefresh:d}),l==="sdk_guide"&&e.jsx(re,{pluginGuideMd:y,loadingMd:A,copiedId:h,handleCopy:k,pluginBoilerplateCode:`# 快速开始:3 分钟创建插件
35
+
36
+ 使用 \`@openlearn/plugin-sdk\` CLI 脚手架,一键生成完整项目:
37
+
38
+ \`\`\`bash
39
+ # 创建插件项目
40
+ npx @openlearn/plugin-sdk init --name hello-world
41
+
42
+ # 进入项目并构建
43
+ cd hello-world
44
+ npm install
45
+ npx @openlearn/plugin-sdk build
46
+
47
+ # 产物:hello-world.zip → 上传到插件中心
48
+ \`\`\`
49
+
50
+ ## 三种模板
51
+
52
+ | 模板 | 说明 | 适用 |
53
+ |------|------|------|
54
+ | \`server-only\` | 纯后端(AI 工具 + 命令) | 数据处理 |
55
+ | \`full-stack\` | 后端 + React 前端 | 完整应用 |
56
+ | \`frontend-only\` | 纯 UI 扩展 | 白板工具 |
57
+
58
+ ## 交互式创建
59
+
60
+ \`\`\`bash
61
+ npx @openlearn/plugin-sdk init
62
+ # CLI 逐步询问:名称 → 描述 → 作者 → 模板
63
+ \`\`\`
64
+
65
+ ## 项目结构
66
+
67
+ \`\`\`
68
+ my-plugin/
69
+ ├── src/index.ts # 服务端入口(export default { manifest, activate })
70
+ ├── src/frontend.tsx # 前端组件(可选)
71
+ └── package.json
72
+ \`\`\`
73
+ `,pluginInteractiveCode:`# 完整插件示例(server-only 模板)
74
+
75
+ \`\`\`typescript
76
+ import type { PluginContext } from '@openlearn/plugin-sdk';
77
+ import {
78
+ ICommandBusServiceToken,
79
+ IActionRegistryServiceToken,
80
+ IEventBusServiceToken,
81
+ IDatabaseToken,
82
+ } from '@openlearn/plugin-sdk';
83
+
84
+ export default {
85
+ manifest: {
86
+ id: '@myorg/hello-plugin',
87
+ name: '问候插件',
88
+ version: '0.1.0',
89
+ requires: [
90
+ '@openlearn/core:ICommandBusService@^1.0.0',
91
+ '@openlearn/core:IActionRegistryService@^1.0.0',
92
+ '@openlearn/core:IEventBusService@^1.0.0',
93
+ '@openlearn/core:IDatabase@^1.0.0',
94
+ ],
95
+ capabilitiesProposed: ['lesson:read'],
96
+ engines: { openlearn: '>=0.2.5' },
97
+ },
98
+
99
+ async activate(ctx: PluginContext) {
100
+ const commandBus = ctx.services.commandBus;
101
+ const actionRegistry = ctx.services.actionRegistry;
102
+ const eventBus = ctx.services.eventBus;
103
+
104
+ // 1. 注册 AI 工具
105
+ await actionRegistry.register({
106
+ id: 'hello-greet',
107
+ commandType: 'hello.greet',
108
+ description: '向指定的人打招呼,返回问候语',
109
+ capabilityRequired: 'lesson:read',
110
+ inputSchema: {
111
+ type: 'OBJECT',
112
+ properties: {
113
+ name: { type: 'STRING', description: '姓名' },
114
+ },
115
+ required: ['name'],
116
+ },
117
+ });
118
+
119
+ // 2. 注册命令处理器
120
+ await commandBus.registerHandler('hello.greet', {
121
+ async execute(command) {
122
+ const payload = command.payload as any;
123
+ const message = \`Hello, \${payload.name}!\`;
124
+
125
+ // 3. 发布事件通知
126
+ await eventBus.publish({
127
+ id: crypto.randomUUID(),
128
+ type: 'hello.greeted',
129
+ source: 'plugin.hello',
130
+ payload: { message },
131
+ timestamp: Date.now(),
132
+ correlationId: command.id,
133
+ });
134
+
135
+ return { message };
136
+ },
137
+ });
138
+
139
+ ctx.log.info('问候插件已激活');
140
+ },
141
+ };
142
+ \`\`\`
143
+
144
+ ## 核心概念:Action → Command → Event
145
+
146
+ \`\`\`
147
+ AI Agent 调用 Action → CommandBus 执行 Handler → EventBus 广播事件
148
+ \`\`\`
149
+
150
+ 1. **Action** — 用 \`actionRegistry.register()\` 注册 AI 工具,\`description\` 是 AI 理解的关键
151
+ 2. **Command** — 用 \`commandBus.registerHandler()\` 执行业务逻辑
152
+ 3. **Event** — 用 \`eventBus.publish()\` 通知其他系统
153
+ `,pluginExamCode:`# 构建与发布
154
+
155
+ \`\`\`bash
156
+ # 开发模式(watch 自动重构建)
157
+ npx @openlearn/plugin-sdk build --watch
158
+
159
+ # 生产构建
160
+ npx @openlearn/plugin-sdk build
161
+
162
+ # 产物
163
+ dist/
164
+ ├── index.js # 服务端 bundle
165
+ ├── frontend.js # 前端 bundle(如有)
166
+ └── my-plugin.zip # ★ 可上传到插件中心
167
+ \`\`\`
168
+
169
+ ## 插件数据库
170
+
171
+ 每个插件有独立的命名空间:
172
+
173
+ \`\`\`typescript
174
+ // 创建表
175
+ await ctx.db.ensureTable('polls', \`
176
+ id TEXT PRIMARY KEY,
177
+ question TEXT NOT NULL
178
+ \`);
179
+
180
+ // 获取带命名空间的表名
181
+ const tableName = ctx.db.table('polls');
182
+ // → plugin_@myorg_hello-plugin_polls
183
+
184
+ // 通过 DI 拿到 raw better-sqlite3
185
+ const db = await ctx.resolve(IDatabaseToken);
186
+ db.prepare(\`INSERT INTO \${tableName} ...\`).run(...);
187
+ \`\`\`
188
+
189
+ ## 可用的 9 个 DI Token
190
+
191
+ 通过 \`ctx.services\` 或 \`ctx.resolve(token)\` 访问:
192
+
193
+ | Token | 服务 |
194
+ |-------|------|
195
+ | ICommandBusServiceToken | 命令总线 |
196
+ | IEventBusServiceToken | 事件总线 |
197
+ | IActionRegistryServiceToken | AI 工具注册 |
198
+ | ICapabilityServiceToken | 权限守卫 |
199
+ | IProcessServiceToken | 后台进程 |
200
+ | IStorageServiceToken | K-V 存储 |
201
+ | IAIServiceToken | AI 文本生成 |
202
+ | IDatabaseToken | SQLite 数据库 |
203
+ | IPluginHostToken | 插件主机管理 |
204
+ `}),l==="user_guide"&&e.jsx(ae,{copiedId:h,handleCopy:k}),l==="plugin_docs"&&e.jsx(ne,{})]})}export{ge as HelpView};
@@ -0,0 +1 @@
1
+ import{j as e}from"./vendor-react-Kv_JATwr.js";import{m as $,C as P}from"./index-2676X8YB.js";import{a8 as b,D,aW as T,aX as O,c as u,o as V,a9 as f}from"./vendor-icons-CXrZ3R2V.js";import"./vendor-utils-aM7aYvPP.js";import"./vendor-pdf-C1Vh2Aqq.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-SrpBsvM3.js";const j=Math.round(P/(1024*1024));function _(y){const{isImportLessonsOpen:N,setIsImportLessonsOpen:o,lang:s,importStatus:a,setIsDraggingImport:m,handleCSVFileChange:F,handleImportFileChange:h,downloadCsvTemplate:v,downloadCourseTemplate:w,packagePreview:t,handlePackageImportSubmit:p,packageDuplicate:n=null,handlePackageDuplicateDismiss:k=()=>{},isDraggingImport:C,previewImportData:l,setPreviewImportData:g,setImportStatus:d,importProgress:i,importProgressTotal:c,importErrorMsg:z,setImportErrorMsg:S,handleCSVImportSubmit:I}=y;return e.jsx(e.Fragment,{children:N&&e.jsx("div",{className:"fixed inset-0 bg-gray-900/50 backdrop-blur-sm flex items-center justify-center p-4 md:p-6 z-50 overflow-y-auto text-gray-850",children:e.jsxs($.div,{initial:{opacity:0,y:15,scale:.98},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:10,scale:.98},className:"bg-white border text-gray-900 border-gray-250 rounded-2xl shadow-2xl w-full max-w-2xl flex flex-col overflow-hidden max-h-[90vh] font-sans text-left",children:[e.jsxs("div",{className:"p-4 md:p-5 border-b border-gray-100 flex items-center justify-between bg-slate-50 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 bg-indigo-50 border border-indigo-100 rounded-lg text-indigo-650",children:e.jsx(b,{size:20,className:"animate-pulse"})}),e.jsxs("div",{children:[e.jsx("h2",{className:"font-bold text-gray-850 text-base md:text-lg",children:s==="zh"?"批量导入课程":"Bulk-Import Courses"}),e.jsx("p",{className:"text-xs text-gray-500 mt-0.5",children:s==="zh"?"CSV 为「简易导入」(仅标题与正文);需要教学环节 + 白板组件 + 课件附件的完整课程请用课程包 JSON。":'CSV is a "quick import" (title and body only). For a complete lesson — segments, whiteboard components and assets — use a course package JSON.'})]})]}),e.jsx("button",{onClick:()=>{a!=="importing"&&o(!1)},disabled:a==="importing",className:"text-gray-400 hover:text-gray-650 font-bold p-1 rounded-lg hover:bg-gray-150 transition-all text-xl leading-none disabled:opacity-40",children:"×"})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-5",children:[a==="idle"&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"p-4 bg-indigo-50/50 border border-indigo-100 rounded-xl",children:[e.jsxs("h4",{className:"text-xs font-bold text-indigo-850 uppercase tracking-wide flex items-center gap-2",children:[e.jsx("span",{children:"📌"}),s==="zh"?"简易导入(CSV)":"Quick import (CSV)",e.jsx("span",{className:"text-[10px] font-mono font-normal normal-case tracking-normal text-indigo-700 bg-indigo-100 border border-indigo-200 rounded px-1.5 py-0.5","data-testid":"csv-simple-import-badge",children:s==="zh"?"仅标题 + 正文":"title + body only"})]}),e.jsx("p",{className:"text-xs text-indigo-900 mt-1 leading-relaxed",children:s==="zh"?"CSV 文件的首行必须定义列标题(不区分大小写、无多余空格),包含以下两项必需内容:":"Your CSV file must include exactly these header columns on the first row (case-insensitive):"}),e.jsxs("ul",{className:"list-disc pl-5 mt-2 text-xs text-indigo-950 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"title"}),":"," ",s==="zh"?'课程名 (非空,例如 "代数几何")':'Course title (Required, e.g. "Linear Algebra")']}),e.jsxs("li",{children:[e.jsx("strong",{children:"content"}),":"," ",s==="zh"?"教学大纲 / Markdown 格式的课堂细目":"Syllabus content supporting rich markdown."]})]}),e.jsxs("div",{className:"mt-3.5 flex flex-wrap gap-2 justify-start",children:[e.jsxs("button",{onClick:v,className:"flex items-center gap-1 p-2 text-xs font-bold text-indigo-700 bg-white border border-indigo-200 rounded-lg shadow-3xs hover:bg-indigo-50 hover:-translate-y-0.5 hover:shadow-xs transition-all cursor-pointer",children:[e.jsx(D,{size:12}),s==="zh"?"获取标准 CSV 模板":"Download Template CSV"]}),e.jsxs("button",{onClick:w,"data-testid":"download-course-package-template-btn",className:"flex items-center gap-1 p-2 text-xs font-bold text-emerald-700 bg-white border border-emerald-200 rounded-lg shadow-3xs hover:bg-emerald-50 hover:-translate-y-0.5 hover:shadow-xs transition-all cursor-pointer",children:[e.jsx(T,{size:12}),s==="zh"?"下载课程模板(JSON)":"Download Course Template (JSON)"]})]}),e.jsx("p",{className:"text-[11px] text-indigo-800/80 mt-2.5 leading-relaxed",children:s==="zh"?"课程模板是一个自包含的 JSON 文件:包含教学环节、全部预设组件的白板元素,以及内嵌的 PDF / PPTX 课件附件 —— 拖回本对话框即可导入成一节完整的课。完整课程统一走这条通道,CSV 只做批量建空壳。":"The course template is a self-contained JSON file: teaching segments, whiteboard elements for every preset component, and embedded PDF/PPTX assets — drop it back here to import a complete lesson. Complete lessons go through this path; CSV only creates shells in bulk."})]}),e.jsxs("div",{onDragOver:r=>{r.preventDefault(),m(!0)},onDragLeave:()=>m(!1),onDrop:r=>{r.preventDefault(),m(!1),r.dataTransfer.files&&r.dataTransfer.files[0]&&h(r.dataTransfer.files[0])},onClick:()=>{document.getElementById("import-csv-file-picker")?.click()},className:`border-2 border-dashed rounded-2xl p-8 text-center cursor-pointer transition-all flex flex-col items-center justify-center min-h-[180px] ${C?"border-indigo-500 bg-indigo-50/70 scale-[1.01]":"border-gray-250 bg-gray-50/50 hover:bg-gray-50 hover:border-indigo-400"}`,children:[e.jsx("input",{id:"import-csv-file-picker",type:"file",accept:".csv,.json",className:"hidden",onChange:r=>{r.target.files&&r.target.files[0]&&h(r.target.files[0])}}),e.jsx("div",{className:"p-3 bg-gray-100 border border-gray-200 rounded-full text-indigo-600 mb-3 group-hover:scale-105 transition-all",children:e.jsx(b,{size:24})}),e.jsx("span",{className:"text-sm font-bold text-gray-800",children:s==="zh"?"选择 CSV / 课程包 JSON 文件,或拖放至此处":"Click to select or drag and drop a CSV / course-package JSON file here"}),e.jsx("span",{className:"text-xs text-gray-400 mt-1",children:s==="zh"?`CSV(简易导入)或课程包 JSON(完整课程),单文件最大 ${j}MB`:`CSV (quick import) or course-package JSON (full lesson), up to ${j}MB`})]})]}),a==="parsing"&&t&&e.jsxs("div",{className:"space-y-4","data-testid":"course-package-preview",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-xs font-bold text-emerald-600 bg-emerald-50 border border-emerald-200 px-2.5 py-1 rounded-full",children:["✓"," ",s==="zh"?`课程包解析成功:${t.segments} 个教学环节 · ${t.elements} 个白板元素`:`Course package parsed: ${t.segments} segments · ${t.elements} elements`]}),e.jsx("button",{onClick:()=>{g([]),d("idle")},className:"text-xs text-indigo-600 hover:text-indigo-800 font-semibold",children:s==="zh"?"重新上传":"Upload Different File"})]}),e.jsxs("div",{className:"p-4 bg-white border border-gray-200 rounded-xl shadow-3xs",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"p-2 bg-emerald-50 border border-emerald-100 rounded-lg text-emerald-600",children:e.jsx(O,{size:18})}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("h4",{className:"text-sm font-bold text-gray-800 truncate",title:t.title,children:t.title}),t.metaName&&e.jsx("p",{className:"text-[11px] text-gray-500 mt-0.5 truncate",children:t.metaName}),e.jsx("p",{className:"text-[11px] text-gray-400 mt-0.5 font-mono truncate",children:t.fileName})]})]}),e.jsxs("dl",{className:"grid grid-cols-3 gap-2 mt-3",children:[e.jsxs("div",{className:"p-2 bg-gray-50 border border-gray-150 rounded-lg",children:[e.jsx("dt",{className:"text-[10px] text-gray-500 font-semibold",children:s==="zh"?"教学环节":"Segments"}),e.jsx("dd",{className:"text-base font-bold text-gray-800",children:t.segments})]}),e.jsxs("div",{className:"p-2 bg-gray-50 border border-gray-150 rounded-lg",children:[e.jsx("dt",{className:"text-[10px] text-gray-500 font-semibold",children:s==="zh"?"白板元素":"Elements"}),e.jsx("dd",{className:"text-base font-bold text-gray-800",children:t.elements})]}),e.jsxs("div",{className:"p-2 bg-gray-50 border border-gray-150 rounded-lg",children:[e.jsx("dt",{className:"text-[10px] text-gray-500 font-semibold",children:s==="zh"?"课件附件":"Assets"}),e.jsx("dd",{className:"text-base font-bold text-gray-800",children:t.assets})]})]}),t.componentTypes.length>0&&e.jsxs("div",{className:"mt-3",children:[e.jsx("p",{className:"text-[10px] text-gray-500 font-semibold mb-1.5",children:s==="zh"?"覆盖的预设组件":"Covered components"}),e.jsx("div",{className:"flex flex-wrap gap-1",children:t.componentTypes.map(r=>e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-indigo-50 border border-indigo-100 text-[10px] font-mono text-indigo-700",children:r},r))})]})]}),e.jsxs("div",{className:"bg-amber-50 border border-amber-100 text-amber-900 text-xs rounded-xl p-3 flex gap-2.5 items-start",children:[e.jsx("span",{className:"text-base leading-none",children:"⚠️"}),e.jsx("p",{className:"leading-relaxed",children:s==="zh"?"导入将以单个事务写入:教学环节、白板元素与课件附件要么全部成功,要么全部回滚,不会留下半截课程。":"The import runs as a single transaction: segments, whiteboard elements and assets are either all written or all rolled back."})]})]}),a==="parsing"&&!t&&l.length>0&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-xs font-bold text-emerald-600 bg-emerald-50 border border-emerald-200 px-2.5 py-1 rounded-full",children:["✓"," ",s==="zh"?`解析成功:查找到 ${l.length} 门课程`:`Parsed Successfully: Found ${l.length} records`]}),e.jsx("button",{onClick:()=>{g([]),d("idle")},className:"text-xs text-indigo-600 hover:text-indigo-800 font-semibold",children:s==="zh"?"重新上传":"Upload Different File"})]}),e.jsx("div",{className:"border border-gray-200 rounded-xl overflow-hidden shadow-3xs max-h-[300px] overflow-y-auto",children:e.jsxs("table",{className:"w-full text-left text-xs border-collapse",children:[e.jsx("thead",{className:"bg-gray-50 text-gray-600 border-b border-gray-200 font-bold",children:e.jsxs("tr",{children:[e.jsx("th",{className:"p-3 w-1/4",children:s==="zh"?"课程名称":"Course Title"}),e.jsx("th",{className:"p-3 w-3/4",children:s==="zh"?"大纲简介片段":"Syllabus Preview"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100 bg-white",children:l.map((r,x)=>e.jsxs("tr",{className:"hover:bg-slate-50/50",children:[e.jsx("td",{className:"p-3 font-semibold text-gray-800 align-top truncate max-w-[150px]",title:r.title,children:r.title}),e.jsx("td",{className:"p-3 text-gray-500 font-mono text-xs leading-relaxed break-words col-span-2",children:r.content.length>150?r.content.substring(0,150)+"...":r.content||e.jsx("em",{className:"text-gray-300 italic",children:"None"})})]},x))})]})}),e.jsxs("div",{className:"bg-amber-50 border border-amber-100 text-amber-900 text-xs rounded-xl p-3 flex gap-2.5 items-start",children:[e.jsx("span",{className:"text-base leading-none",children:"⚠️"}),e.jsx("p",{className:"leading-relaxed",children:s==="zh"?'请确认课程名称没有与系统已有的课程同名。确认无误后点击下方"开始导入"写入 SQLite。':"Please ensure column details are accurate. Clicking Import will instantly commit all parsed courses into the server SQLite backend."})]})]}),a==="importing"&&t&&e.jsxs("div",{className:"py-8 flex flex-col items-center justify-center space-y-4",children:[e.jsx("div",{className:"relative w-20 h-20 flex items-center justify-center",children:e.jsx(u,{size:36,className:"text-indigo-600 animate-spin"})}),e.jsxs("div",{className:"text-center",children:[e.jsx("h3",{className:"font-bold text-gray-800 text-sm",children:s==="zh"?"正在导入课程包":"Importing course package"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:s==="zh"?`正在写入 ${t.segments} 个教学环节、${t.elements} 个白板元素与 ${t.assets} 份课件附件...`:`Writing ${t.segments} segments, ${t.elements} elements and ${t.assets} assets...`})]}),e.jsx("div",{className:"w-full max-w-md bg-gray-50 rounded-xl p-3 border border-gray-150 font-mono text-xs text-gray-400",children:e.jsx("div",{children:"[API] POST /api/course-packages/import -> single transaction..."})})]}),a==="importing"&&!t&&e.jsxs("div",{className:"py-8 flex flex-col items-center justify-center space-y-4",children:[e.jsxs("div",{className:"relative w-20 h-20 flex items-center justify-center",children:[e.jsx(u,{size:36,className:"text-indigo-600 animate-spin"}),e.jsxs("span",{className:"absolute text-xs font-extrabold text-indigo-700",children:[Math.round(i/c*100),"%"]})]}),e.jsxs("div",{className:"text-center",children:[e.jsx("h3",{className:"font-bold text-gray-800 text-sm",children:s==="zh"?"正在写入数据库":"Populating Database Records"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1",children:s==="zh"?`正在导入第 ${i} / ${c} 项...`:`Importing item ${i} of ${c}...`})]}),e.jsx("div",{className:"w-full max-w-sm bg-gray-100 h-2 rounded-full overflow-hidden border border-gray-200",children:e.jsx("div",{className:"bg-indigo-600 h-full transition-all duration-300 rounded-full",style:{width:`${i/c*100}%`}})}),e.jsxs("div",{className:"w-full max-w-md bg-gray-50 rounded-xl p-3 border border-gray-150 font-mono text-xs text-gray-400 max-h-[140px] overflow-y-auto",children:[e.jsx("div",{children:"[API] POST /api/lessons -> one request per course (no cross-course transaction)"}),l.slice(0,i).map((r,x)=>e.jsx("div",{className:"text-indigo-600 font-bold mt-1",children:`✓ [${x+1}] "${r.title}" -> status 200 (Success)`},x))]})]}),a==="success"&&e.jsxs("div",{className:"py-8 text-center flex flex-col items-center justify-center space-y-4",children:[e.jsx("div",{className:"w-16 h-16 bg-emerald-50 rounded-full flex items-center justify-center border border-emerald-200 text-emerald-600 animate-bounce",children:e.jsx(V,{size:32})}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-bold text-gray-800 text-base",children:s==="zh"?"🎉 批量导入大功告成":"🎉 Bulk-Import Complete"}),e.jsx("p",{className:"text-xs text-gray-500 mt-1 max-w-md mx-auto",children:t?s==="zh"?`课程包已导入:${t.segments} 个教学环节、${t.elements} 个白板元素、${t.assets} 份课件附件均已写入。`:`Course package imported: ${t.segments} segments, ${t.elements} elements and ${t.assets} assets written.`:s==="zh"?`所有 ${l.length} 门学科教案数据已顺畅写入系统底层 SQLite 数据仓库,现在已可以用于备课。`:`All ${l.length} curriculum lessons records have been successfully saved into security logs and SQLite storage.`})]})]}),a==="error"&&e.jsxs("div",{className:"space-y-4",children:[n?e.jsxs("div",{className:"p-4 bg-amber-50 border border-amber-100 rounded-xl flex gap-3 items-start","data-testid":"course-package-duplicate-notice",children:[e.jsx("span",{className:"text-amber-600 font-bold text-lg leading-none",children:"⧉"}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-amber-850",children:s==="zh"?"该课程包已经导入过了":"This course package was already imported"}),e.jsx("p",{className:"text-xs text-amber-900 mt-1 leading-relaxed",children:s==="zh"?e.jsxs(e.Fragment,{children:["已存在课程「",n.lessonTitle,"」",n.hasDeclaredId?"(同一个课程包 id)":"(内容完全相同)",", 再次导入会得到两节内容相同的课程。"]}):e.jsxs(e.Fragment,{children:['A lesson named "',n.lessonTitle,'" already exists',n.hasDeclaredId?" (same course-package id)":" (identical content)",". Importing again would create two lessons with the same content."]})})]})]}):e.jsxs("div",{className:"p-4 bg-rose-50 border border-rose-100 rounded-xl flex gap-3 items-start",children:[e.jsx("span",{className:"text-rose-600 font-bold text-lg leading-none",children:"⚠️"}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-xs font-bold text-rose-850",children:s==="zh"?"数据导入或解析中断":"Import or Parsing Error"}),e.jsx("p",{className:"text-xs text-rose-900 mt-1 leading-relaxed",children:z||(s==="zh"?"未知异常或文件破损。":"An unknown exception or corrupted CSV formatting occurred.")})]})]}),e.jsx("div",{className:"flex gap-3 justify-end",children:n?e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:k,"data-testid":"course-package-duplicate-cancel",className:"px-4 py-2 text-xs font-bold text-gray-700 bg-white border border-gray-250 cursor-pointer hover:bg-gray-50 rounded-lg transition-all",children:s==="zh"?"取消":"Cancel"}),e.jsx("button",{onClick:()=>p(!0),"data-testid":"course-package-duplicate-force",className:"px-4 py-2 text-xs font-bold text-white bg-amber-600 cursor-pointer hover:bg-amber-700 rounded-lg transition-all",children:s==="zh"?"仍然导入副本":"Import as a Copy Anyway"})]}):e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>{d("idle"),S("")},className:"px-4 py-2 text-xs font-bold text-gray-700 bg-white border border-gray-250 cursor-pointer hover:bg-gray-50 rounded-lg transition-all",children:s==="zh"?"返回重试":"Go Back & Retry"}),e.jsx("button",{onClick:()=>o(!1),className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 cursor-pointer hover:bg-indigo-700 rounded-lg transition-all",children:s==="zh"?"关闭窗口":"Close"})]})})]})]}),a!=="idle"&&a!=="error"&&e.jsxs("div",{className:"p-4 md:p-5 border-t border-gray-100 flex items-center justify-between bg-slate-50 shrink-0",children:[e.jsx("button",{onClick:()=>{g([]),d("idle")},disabled:a==="importing",className:"px-4 py-2 text-xs font-bold text-gray-650 bg-white border border-gray-200 hover:bg-gray-100 rounded-lg transition-all disabled:opacity-40 cursor-pointer",children:s==="zh"?"重置重选":"Reset & Clear"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:()=>o(!1),disabled:a==="importing",className:"px-4 py-2 text-xs font-bold text-gray-600 hover:text-gray-800 transition-all disabled:opacity-40 cursor-pointer",children:s==="zh"?"取消":"Cancel"}),a==="parsing"&&t&&e.jsxs("button",{onClick:()=>p(!1),"data-testid":"import-course-package-btn",className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-all flex items-center gap-1 cursor-pointer",children:[e.jsx(f,{size:12}),s==="zh"?"导入课程包":"Import Course Package"]}),a==="parsing"&&!t&&e.jsxs("button",{onClick:I,className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-all flex items-center gap-1 cursor-pointer",children:[e.jsx(f,{size:12}),s==="zh"?`开始导入 (${l.length} 类)`:`Proceed and Import (${l.length})`]}),a==="success"&&e.jsx("button",{onClick:()=>o(!1),className:"px-4 py-2 text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 rounded-lg shadow-sm transition-all cursor-pointer",children:s==="zh"?"完成":"Done"})]})]})]})})})}export{_ as ImportLessonsModal};
@@ -0,0 +1,2 @@
1
+ import{j as e}from"./vendor-react-Kv_JATwr.js";import{a8 as d,D as g,v as x,a9 as h,c as f}from"./vendor-icons-CXrZ3R2V.js";import"./vendor-utils-aM7aYvPP.js";function j({show:c,onClose:r,lang:s,handleImportFile:a,importError:n,importSuccess:l,isImporting:m,downloadCSVTemplate:i}){return c?e.jsx("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 font-sans text-gray-800",children:e.jsxs("div",{className:"bg-white rounded-xl shadow-xl w-full max-w-lg flex flex-col p-6 relative",children:[e.jsx("button",{type:"button",onClick:r,className:"absolute top-4 right-4 text-gray-400 hover:text-gray-600 text-xl font-medium cursor-pointer",children:"×"}),e.jsxs("h3",{className:"text-base font-semibold text-gray-900 mb-1 flex items-center gap-2",children:[e.jsx(d,{size:18,className:"text-indigo-600"}),s==="zh"?"手动批量导入数据":"Manual Bulk Import"]}),e.jsx("p",{className:"text-xs text-gray-500 mb-4 font-sans text-left",children:s==="zh"?"支持导入 CSV 或 JSON 文件。CSV 文件需包含标题行(支持中英文标题),仅「学生姓名」为必填,其余列可选:":'Supports CSV or JSON files. Only "Student Name" is required; all other columns are optional:'}),e.jsxs("div",{className:"bg-gray-50 p-3 rounded-lg border border-gray-100 text-xs font-mono text-gray-600 mb-4 whitespace-pre overflow-x-auto select-all text-left leading-relaxed",children:["班级名称, 班级描述, 学生学号, 学生姓名, 学生邮箱, 初始密码, 学生备注",`
2
+ `,"高一A班, 基础英语课程, 20260101, 李明, liming@example.com, , 数学较弱"]}),e.jsx("ul",{className:"mb-4 text-xs text-gray-500 space-y-1 list-disc pl-5 font-sans text-left",children:s==="zh"?e.jsxs(e.Fragment,{children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-gray-700",children:"学号"}),":学生登录账号。留空则自动生成; 已存在同",e.jsx("strong",{className:"text-gray-700",children:"邮箱"}),"或同学号的学生会被识别为同一人,不会重复建档。"]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-gray-700",children:"初始密码"}),":留空则系统生成 12 位随机密码, 并在导入成功提示中列出「学号 / 姓名 / 密码」供你分发(仅显示一次,请及时保存)。"]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-gray-700",children:"学生备注"}),":仅本平台教师可见的私有备注。"]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-gray-700",children:"Student Number"})," is the login account. Auto-generated if blank; students already matching by ",e.jsx("strong",{className:"text-gray-700",children:"email"})," or student number are reused instead of duplicated."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-gray-700",children:"Initial Password"}),": leave blank and a 12-character random password is generated and listed once in the success message for you to distribute."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-gray-700",children:"Student Notes"})," is a teacher-only private note."]})]})}),e.jsxs("div",{className:"mb-4 flex flex-col gap-2 p-3 bg-indigo-50/50 border border-indigo-100 rounded-lg text-left select-none",children:[e.jsxs("span",{className:"text-xs font-semibold text-indigo-900 flex items-center gap-1",children:[e.jsx(g,{size:13,className:"text-indigo-600"}),s==="zh"?"下载导入数据模板文件:":"Download Import Templates:"]}),e.jsxs("div",{className:"flex flex-col sm:flex-row gap-2 mt-1",children:[e.jsxs("button",{type:"button",onClick:()=>i("class"),className:"flex-1 flex items-center justify-center gap-1.5 text-xs px-2.5 py-2 bg-white border border-indigo-200 text-indigo-700 rounded-lg hover:bg-indigo-50 transition-colors shadow-sm font-medium cursor-pointer",children:[e.jsx(x,{size:12,className:"text-emerald-600"}),s==="zh"?"下载班级与学生模板":"Class & Students Template"]}),e.jsxs("button",{type:"button",onClick:()=>i("student"),className:"flex-1 flex items-center justify-center gap-1.5 text-xs px-2.5 py-2 bg-white border border-indigo-200 text-indigo-700 rounded-lg hover:bg-indigo-50 transition-colors shadow-sm font-medium cursor-pointer",children:[e.jsx(x,{size:12,className:"text-emerald-600"}),s==="zh"?"下载独立学生模板":"Independent Students Template"]})]})]}),e.jsxs("div",{onDragOver:t=>t.preventDefault(),onDrop:t=>{t.preventDefault(),!(!t.dataTransfer.files||t.dataTransfer.files.length===0)&&a(t.dataTransfer.files[0])},className:"border-2 border-dashed border-gray-300 rounded-xl p-8 text-center cursor-pointer hover:border-indigo-500 hover:bg-indigo-50/20 transition-all mb-4",onClick:()=>{const t=document.createElement("input");t.type="file",t.accept=".csv,.json",t.onchange=o=>{o.target.files?.[0]&&a(o.target.files[0])},t.click()},children:[e.jsx(d,{className:"mx-auto text-gray-400 mb-2",size:24}),e.jsx("p",{className:"text-xs font-semibold text-gray-700",children:s==="zh"?"点击选择文件 或 拖拽文件到这里":"Click to select file or drag it here"}),e.jsx("p",{className:"text-xs text-gray-400 mt-1",children:"CSV or JSON (max 5MB)"})]}),n&&e.jsxs("div",{className:"p-3 bg-red-50 border border-red-100 text-red-700 text-xs rounded-lg mb-4 flex items-start gap-2 max-h-32 overflow-y-auto",children:[e.jsx("span",{className:"font-bold shrink-0",children:"✕"}),e.jsx("span",{className:"font-sans text-left leading-tight break-all",children:n})]}),l&&e.jsxs("div",{className:"p-3 bg-green-50 border border-green-105 text-green-700 text-xs rounded-lg mb-4 flex items-start gap-2",children:[e.jsx(h,{size:14,className:"shrink-0 mt-0.5"}),e.jsx("span",{className:"font-sans text-left leading-tight",children:l})]}),m&&e.jsxs("div",{className:"flex items-center justify-center gap-2 text-indigo-600 text-xs py-2",children:[e.jsx(f,{size:16,className:"animate-spin"}),e.jsx("span",{children:s==="zh"?"正在分析数据并导入系统数据库...":"Validating data and transferring records..."})]}),e.jsx("div",{className:"flex justify-end gap-2 mt-2",children:e.jsx("button",{type:"button",onClick:r,className:"px-4 py-2 text-xs font-medium border border-gray-200 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors cursor-pointer",children:s==="zh"?"关闭":"Close"})})]})}):null}export{j as ImportModal};
@@ -0,0 +1 @@
1
+ import{r as n,j as e}from"./vendor-react-Kv_JATwr.js";import{a1 as j,a2 as v,E as x,a3 as y,a4 as k}from"./index-2676X8YB.js";import{ao as u,l as N,m as E,X as F}from"./vendor-icons-CXrZ3R2V.js";import"./vendor-utils-aM7aYvPP.js";import"./vendor-pdf-C1Vh2Aqq.js";import"./vendor-motion-BFAPOs-b.js";import"./vendor-charts-SrpBsvM3.js";const h=new Map,C=45*1e3;function q({coursewareId:r,onClose:d}){const[m,g]=n.useState(!1),[o,l]=n.useState(null),a=n.useRef(null),i=n.useRef(null),{theme:f}=j();n.useEffect(()=>{if(i.current)return v(i.current)},[o]);const p=()=>{y(f,k(f))};n.useEffect(()=>{const t=()=>{g(!!document.fullscreenElement&&document.fullscreenElement===a.current)};return document.addEventListener("fullscreenchange",t),()=>{document.removeEventListener("fullscreenchange",t)}},[]),n.useEffect(()=>{let t=!1;if(!r){l(null);return}const c=h.get(r),w=Date.now();if(c&&c.expiresAt>w){l(c.token);return}return l(null),fetch(`/api/courseware/${encodeURIComponent(r)}/access-token`).then(s=>s.ok?s.json():Promise.reject(new Error(`HTTP ${s.status}`))).then(s=>{!t&&typeof s?.token=="string"&&s.token&&(h.set(r,{token:s.token,expiresAt:Date.now()+C}),l(s.token))}).catch(s=>{console.warn("[InteractiveCoursewareViewer] Failed to mint courseware access token:",s)}),()=>{t=!0}},[r]);const b=()=>{const t=a.current;t&&(document.fullscreenElement?document.exitFullscreen?document.exitFullscreen():document.webkitExitFullscreen&&document.webkitExitFullscreen():t.requestFullscreen?t.requestFullscreen():t.webkitRequestFullscreen&&t.webkitRequestFullscreen())};return r?e.jsxs("div",{ref:a,className:"flex flex-col h-full bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden relative",children:[e.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-gray-100 border-b border-gray-200 shrink-0",children:[e.jsxs("div",{className:"flex items-center gap-2 select-none",children:[e.jsx(u,{size:16,className:"text-indigo-500"}),e.jsx("span",{className:"font-semibold text-sm text-gray-700",children:"Interactive Courseware"})]}),e.jsx(x,{slot:"courseware.viewer.toolbar",slotProps:{coursewareId:r,classId:null,lessonId:null}}),e.jsxs("div",{className:"flex items-center gap-2 ml-auto",children:[e.jsx("button",{onClick:b,className:"p-1 hover:bg-gray-200 rounded-lg text-gray-500 transition-colors cursor-pointer",title:m?"退出全屏":"全屏播放",children:m?e.jsx(N,{size:16}):e.jsx(E,{size:16})}),d&&e.jsx("button",{onClick:d,className:"p-1 hover:bg-gray-200 rounded-lg text-gray-500 transition-colors cursor-pointer",children:e.jsx(F,{size:16})})]})]}),e.jsxs("div",{className:"flex-1 relative bg-white",children:[e.jsx("div",{className:"absolute inset-0 pointer-events-none overflow-hidden",children:e.jsx(x,{slot:"courseware.viewer.overlay",slotProps:{coursewareId:r}})}),o?e.jsx("iframe",{ref:i,src:`/api/courseware/${r}?ct=${encodeURIComponent(o)}`,sandbox:"allow-scripts allow-forms allow-downloads","data-lms-bridge":"true",loading:"lazy",referrerPolicy:"no-referrer",allowFullScreen:!0,className:"w-full h-full border-none transition-opacity duration-300",title:"Interactive Courseware",onLoad:p}):e.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center bg-slate-50/80 backdrop-blur-xs select-none",children:[e.jsxs("div",{className:"relative flex items-center justify-center",children:[e.jsx("div",{className:"w-10 h-10 border-3 border-indigo-200 border-t-indigo-600 rounded-full animate-spin"}),e.jsx(u,{size:18,className:"absolute text-indigo-500 opacity-80"})]}),e.jsx("span",{className:"mt-3 text-xs font-medium text-slate-500 tracking-wide animate-pulse",children:"正在准备课件沙箱环境…"})]})]})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center h-full text-gray-400 bg-gray-50 rounded-xl border border-dashed border-gray-300",children:[e.jsx(u,{size:48,className:"mb-4 text-gray-300 opacity-50"}),e.jsx("h3",{className:"text-lg font-medium text-gray-700",children:"No Courseware Selected"}),e.jsx("p",{className:"mt-2 text-sm text-center",children:"Please select a courseware from the list to view it interactively."})]})}export{q as InteractiveCoursewareViewer,q as default};