@vue-skuilder/platform-ui 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{About-DOVn5O6M.js → About-BX8K6rwb.js} +2 -2
- package/dist/assets/{About-DOVn5O6M.js.map → About-BX8K6rwb.js.map} +1 -1
- package/dist/assets/{AdminDashboard-BG3M28eh.js → AdminDashboard-W0SPEmuP.js} +2 -2
- package/dist/assets/{AdminDashboard-BG3M28eh.js.map → AdminDashboard-W0SPEmuP.js.map} +1 -1
- package/dist/assets/{ClassroomCtrlPanel-CnJ9dZWC.js → ClassroomCtrlPanel-cm7FrebK.js} +2 -2
- package/dist/assets/{ClassroomCtrlPanel-CnJ9dZWC.js.map → ClassroomCtrlPanel-cm7FrebK.js.map} +1 -1
- package/dist/assets/{Classrooms-XbwgxDfM.js → Classrooms-TyXJEif8.js} +2 -2
- package/dist/assets/{Classrooms-XbwgxDfM.js.map → Classrooms-TyXJEif8.js.map} +1 -1
- package/dist/assets/{CourseRouter-7RfEhGGY.js → CourseRouter-B1Wb2SVh.js} +2 -2
- package/dist/assets/{CourseRouter-7RfEhGGY.js.map → CourseRouter-B1Wb2SVh.js.map} +1 -1
- package/dist/assets/{CourseWare-BTFRjgBR-Bm9K1dlj.js → CourseWare-BTFRjgBR-BM2ndwkk.js} +3 -3
- package/dist/assets/{CourseWare-BTFRjgBR-Bm9K1dlj.js.map → CourseWare-BTFRjgBR-BM2ndwkk.js.map} +1 -1
- package/dist/assets/{Courses-g6uz0ftQ.js → Courses-D7PcUvjf.js} +2 -2
- package/dist/assets/{Courses-g6uz0ftQ.js.map → Courses-D7PcUvjf.js.map} +1 -1
- package/dist/assets/{EloModeration-Cn0Gke9Q.js → EloModeration-BYQiqWIc.js} +2 -2
- package/dist/assets/{EloModeration-Cn0Gke9Q.js.map → EloModeration-BYQiqWIc.js.map} +1 -1
- package/dist/assets/{JoinCode-DjHHYAUt.js → JoinCode-C3sgX1_b.js} +2 -2
- package/dist/assets/{JoinCode-DjHHYAUt.js.map → JoinCode-C3sgX1_b.js.map} +1 -1
- package/dist/assets/{NewCourseDialog-CQ01235U.js → NewCourseDialog-D2EFpGW3.js} +2 -2
- package/dist/assets/{NewCourseDialog-CQ01235U.js.map → NewCourseDialog-D2EFpGW3.js.map} +1 -1
- package/dist/assets/{ReleaseNotes-Ch5reB70.js → ReleaseNotes-DiEnMdUo.js} +2 -2
- package/dist/assets/{ReleaseNotes-Ch5reB70.js.map → ReleaseNotes-DiEnMdUo.js.map} +1 -1
- package/dist/assets/{RequestPasswordReset-lhJOIdlr.js → RequestPasswordReset-COkRLnI5.js} +2 -2
- package/dist/assets/{RequestPasswordReset-lhJOIdlr.js.map → RequestPasswordReset-COkRLnI5.js.map} +1 -1
- package/dist/assets/{ResetPassword-Ch8K9f_W.js → ResetPassword-gL7oePAm.js} +2 -2
- package/dist/assets/{ResetPassword-Ch8K9f_W.js.map → ResetPassword-gL7oePAm.js.map} +1 -1
- package/dist/assets/{Study-kn4Qa6NR.js → Study-qsc2DimY.js} +2 -2
- package/dist/assets/{Study-kn4Qa6NR.js.map → Study-qsc2DimY.js.map} +1 -1
- package/dist/assets/{TagInformation-B3c3w-f-.js → TagInformation-CGb-OiBj.js} +2 -2
- package/dist/assets/{TagInformation-B3c3w-f-.js.map → TagInformation-CGb-OiBj.js.map} +1 -1
- package/dist/assets/{User-B52kGSvs.js → User-vDLmhUqz.js} +2 -2
- package/dist/assets/{User-B52kGSvs.js.map → User-vDLmhUqz.js.map} +1 -1
- package/dist/assets/{UserStats-C1yX5ng2.js → UserStats-DwSII4oJ.js} +2 -2
- package/dist/assets/{UserStats-C1yX5ng2.js.map → UserStats-DwSII4oJ.js.map} +1 -1
- package/dist/assets/{VerifyEmail-BDok6swO.js → VerifyEmail-PaS24KNr.js} +2 -2
- package/dist/assets/{VerifyEmail-BDok6swO.js.map → VerifyEmail-PaS24KNr.js.map} +1 -1
- package/dist/assets/{chess-E2uOAyQS-C-skdCEx.js → chess-E2uOAyQS-D6-nU3Ft.js} +2 -2
- package/dist/assets/{chess-E2uOAyQS-C-skdCEx.js.map → chess-E2uOAyQS-D6-nU3Ft.js.map} +1 -1
- package/dist/assets/chess-Lfm7Fsvs-BO_caRYF.js +1 -0
- package/dist/assets/common-ui.es-CSYklgoS.js +1 -0
- package/dist/assets/{common-ui.es-DJSq5P5t.js → common-ui.es-DVeFVpJu.js} +4 -4
- package/dist/assets/{common-ui.es-DJSq5P5t.js.map → common-ui.es-DVeFVpJu.js.map} +1 -1
- package/dist/assets/dist-Bir8Vj83.js +1 -0
- package/dist/assets/{dist-B00Hqyz-.js → dist-BrQ2uZNM.js} +7 -6
- package/dist/assets/{dist-B00Hqyz-.js.map → dist-BrQ2uZNM.js.map} +1 -1
- package/dist/assets/{dist-8MeMo0wo.js → dist-Bvalsyqw.js} +3 -3
- package/dist/assets/{dist-8MeMo0wo.js.map → dist-Bvalsyqw.js.map} +1 -1
- package/dist/assets/dist-p3nQRbSv.js +1 -0
- package/dist/assets/{edit-ui.es-D8vCYoTS.js → edit-ui.es-D8UK3wki.js} +2 -2
- package/dist/assets/{edit-ui.es-D8vCYoTS.js.map → edit-ui.es-D8UK3wki.js.map} +1 -1
- package/dist/assets/edit-ui.es-JYSdUjaE.js +1 -0
- package/dist/assets/{french-Dk7YG8Td-D7hmbXe_.js → french-Dk7YG8Td-9xDoM4sO.js} +2 -2
- package/dist/assets/{french-Dk7YG8Td-D7hmbXe_.js.map → french-Dk7YG8Td-9xDoM4sO.js.map} +1 -1
- package/dist/assets/french-evUMlbbq-Df9BUsJG.js +1 -0
- package/dist/assets/{index-CaM5X4qV.js → index-ICRI9oIm.js} +4 -4
- package/dist/assets/{index-CaM5X4qV.js.map → index-ICRI9oIm.js.map} +1 -1
- package/dist/assets/{math-B4HbgYf6-BLw53-qa.js → math-B4HbgYf6-COGto2OX.js} +2 -2
- package/dist/assets/{math-B4HbgYf6-BLw53-qa.js.map → math-B4HbgYf6-COGto2OX.js.map} +1 -1
- package/dist/assets/math-DYni7rRl-CRJfVHOM.js +1 -0
- package/dist/assets/piano-BN5Btq91-8k9MvtW9.js +1 -0
- package/dist/assets/{piano-DF1g6yaX-CcTigzRT.js → piano-DF1g6yaX-Dq5MQT_r.js} +2 -2
- package/dist/assets/{piano-DF1g6yaX-CcTigzRT.js.map → piano-DF1g6yaX-Dq5MQT_r.js.map} +1 -1
- package/dist/assets/{pitch-CgGJFkZ1-NxSC-TNM.js → pitch-CgGJFkZ1-Dn29kwa0.js} +2 -2
- package/dist/assets/{pitch-CgGJFkZ1-NxSC-TNM.js.map → pitch-CgGJFkZ1-Dn29kwa0.js.map} +1 -1
- package/dist/assets/pitch-Dn0iNqiS-C14KjLXT.js +1 -0
- package/dist/assets/{server-BhPiicHd.js → server-BWluIxm_.js} +2 -2
- package/dist/assets/{server-BhPiicHd.js.map → server-BWluIxm_.js.map} +1 -1
- package/dist/assets/{sightsing-BFQ7HRig-BP1WHTrL.js → sightsing-BFQ7HRig-C67T4owN.js} +2 -2
- package/dist/assets/{sightsing-BFQ7HRig-BP1WHTrL.js.map → sightsing-BFQ7HRig-C67T4owN.js.map} +1 -1
- package/dist/assets/sightsing-CJX3k0Fd-BxTgtvY4.js +1 -0
- package/dist/assets/{typing-BevtfWlp-BygtodGx.js → typing-BevtfWlp-CXqCxI5O.js} +2 -2
- package/dist/assets/{typing-BevtfWlp-BygtodGx.js.map → typing-BevtfWlp-CXqCxI5O.js.map} +1 -1
- package/dist/assets/typing-k-ojjO7G-jBfUYnwF.js +1 -0
- package/dist/assets/{word-work-BOnRlZgd-ghpydlV6.js → word-work-BOnRlZgd-BDU1Paky.js} +2 -2
- package/dist/assets/{word-work-BOnRlZgd-ghpydlV6.js.map → word-work-BOnRlZgd-BDU1Paky.js.map} +1 -1
- package/dist/assets/word-work-Dd6tIKrt-DH2IzRC8.js +1 -0
- package/dist/index.html +3 -3
- package/dist/sw.js +1 -1
- package/dist/sw.js.map +1 -1
- package/package.json +7 -7
- package/dist/assets/chess-Lfm7Fsvs-B0MtIbq2.js +0 -1
- package/dist/assets/common-ui.es-B0LOXTBA.js +0 -1
- package/dist/assets/dist-DByb8zkY.js +0 -1
- package/dist/assets/dist-qi8-HFTs.js +0 -1
- package/dist/assets/edit-ui.es-DTMa7NI6.js +0 -1
- package/dist/assets/french-evUMlbbq-wPMGD4UX.js +0 -1
- package/dist/assets/math-DYni7rRl-DIUfZuit.js +0 -1
- package/dist/assets/piano-BN5Btq91-DuFgSFWz.js +0 -1
- package/dist/assets/pitch-Dn0iNqiS-cu_qfJ6X.js +0 -1
- package/dist/assets/sightsing-CJX3k0Fd-DvYFijgM.js +0 -1
- package/dist/assets/typing-k-ojjO7G-C6hEOQy5.js +0 -1
- package/dist/assets/word-work-Dd6tIKrt-BXFli1pb.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./dist-leWgtXBf.js";import{n as e}from"./dist-BrQ2uZNM.js";export{e as CourseLookup};
|
|
@@ -340,13 +340,14 @@ Example:
|
|
|
340
340
|
window.skuilder.mixer.showLastMix()
|
|
341
341
|
window.skuilder.mixer.explainSourceBalance()
|
|
342
342
|
window.skuilder.mixer.compareScores()
|
|
343
|
-
`)}};function mountMixerDebugger(){if(typeof window>`u`)return;let e=window;e.skuilder=e.skuilder||{},e.skuilder.mixer=mixerDebugAPI}mountMixerDebugger(),init_logger(),init_PipelineDebugger();var activeSession=null,sessionHistory=[],MAX_HISTORY=5;function startSessionTracking(e,t,n){clearRunHistory();let r=`session-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;activeSession={sessionId:r,startTime:new Date,initialQueues:{timestamp:new Date,reviewQLength:e,newQLength:t,failedQLength:n},presentations:[],queueSnapshots:[]},logger.debug(`[SessionDebugger] Started tracking session: ${r}`)}function recordCardPresentation(e,t,n,r,a,o){if(!activeSession){logger.warn(`[SessionDebugger] No active session to record presentation`);return}activeSession.presentations.push({timestamp:new Date,sequenceNumber:activeSession.presentations.length+1,cardId:e,courseId:t,courseName:n,origin:r,queueSource:a,score:o})}function snapshotQueues(e,t,n,r,a){activeSession&&activeSession.queueSnapshots.push({timestamp:new Date,reviewQLength:e,newQLength:t,failedQLength:n,reviewQNext3:r,newQNext3:a})}function endSessionTracking(){activeSession&&(activeSession.endTime=new Date,sessionHistory.unshift(activeSession),sessionHistory.length>MAX_HISTORY&&sessionHistory.pop(),logger.debug(`[SessionDebugger] Ended session: ${activeSession.sessionId}`),activeSession=null)}function showCurrentQueue(){if(!activeSession){logger.info(`[Session Debug] No active session.`);return}let e=activeSession.queueSnapshots[activeSession.queueSnapshots.length-1]||activeSession.initialQueues;console.group(`📊 Current Queue State`),logger.info(`Review Queue: ${e.reviewQLength} cards`),e.reviewQNext3&&e.reviewQNext3.length>0&&logger.info(` Next: ${e.reviewQNext3.join(`, `)}`),logger.info(`New Queue: ${e.newQLength} cards`),e.newQNext3&&e.newQNext3.length>0&&logger.info(` Next: ${e.newQNext3.join(`, `)}`),logger.info(`Failed Queue: ${e.failedQLength} cards`),console.groupEnd()}function showPresentationHistory(e=0){let t=e===0&&activeSession?activeSession:sessionHistory[e];if(!t){logger.info(`[Session Debug] No session found at index ${e}`);return}console.group(`\u{1F4DC} Session History: ${t.sessionId}`),logger.info(`Started: ${t.startTime.toLocaleTimeString()}`),t.endTime&&logger.info(`Ended: ${t.endTime.toLocaleTimeString()}`),logger.info(`Cards presented: ${t.presentations.length}`),t.presentations.length>0&&console.table(t.presentations.map(e=>({"#":e.sequenceNumber,course:e.courseName||e.courseId.slice(0,8),origin:e.origin,queue:e.queueSource,score:e.score?.toFixed(3)||`-`,time:e.timestamp.toLocaleTimeString()}))),console.groupEnd()}function showInterleaving(e=0){let t=e===0&&activeSession?activeSession:sessionHistory[e];if(!t){logger.info(`[Session Debug] No session found at index ${e}`);return}console.group(`🔀 Interleaving Analysis`);let n=new Map,r=new Map;if(t.presentations.forEach(e=>{let t=e.courseName||e.courseId;n.set(t,(n.get(t)||0)+1),r.has(t)||r.set(t,{review:0,new:0,failed:0});let a=r.get(t);a[e.origin]++}),logger.info(`Course distribution:`),console.table(Array.from(n.entries()).map(([e,n])=>{let a=r.get(e);return{course:e,total:n,reviews:a.review,new:a.new,failed:a.failed,percentage:(n/t.presentations.length*100).toFixed(1)+`%`}})),t.presentations.length>0){logger.info(`
|
|
343
|
+
`)}};function mountMixerDebugger(){if(typeof window>`u`)return;let e=window;e.skuilder=e.skuilder||{},e.skuilder.mixer=mixerDebugAPI}mountMixerDebugger(),init_logger(),init_PipelineDebugger(),init_logger();var activeController=null;function registerActiveController(e){activeController=e}function getActiveController(){return activeController}var OVERLAY_ID=`skuilder-session-overlay`,POLL_MS=300,INLINE_THRESHOLD=5,SPINNER_FRAMES=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],spinnerFrame=0,overlayEl=null,pollHandle=null,expanded={reviewQ:!1,newQ:!1,failedQ:!1};function toggleSessionOverlay(){if(typeof document>`u`){logger.info(`[Session Overlay] No DOM available (non-browser host); overlay unavailable.`);return}overlayEl?(teardown(),logger.info(`[Session Overlay] Hidden.`)):(mount(),logger.info(`[Session Overlay] Shown. Toggle off with window.skuilder.session.dbgOverlay().`))}function mount(){overlayEl=document.createElement(`div`),overlayEl.id=OVERLAY_ID,Object.assign(overlayEl.style,{position:`fixed`,top:`8px`,left:`8px`,zIndex:`2147483647`,maxWidth:`320px`,maxHeight:`90vh`,overflowY:`auto`,padding:`8px 10px`,background:`rgba(17, 24, 39, 0.92)`,color:`#e5e7eb`,font:`11px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace`,borderRadius:`6px`,boxShadow:`0 4px 16px rgba(0,0,0,0.4)`,pointerEvents:`auto`,userSelect:`none`}),document.body.appendChild(overlayEl),render(),pollHandle=setInterval(render,POLL_MS)}function teardown(){pollHandle!==null&&(clearInterval(pollHandle),pollHandle=null),overlayEl?.parentNode&&overlayEl.parentNode.removeChild(overlayEl),overlayEl=null}function render(){if(!overlayEl)return;spinnerFrame++;let e=getActiveController();if(!e){overlayEl.innerHTML=headerHtml()+`<div style="opacity:.65">No active session.</div>`;return}let t=e.getDebugSnapshot();overlayEl.innerHTML=headerHtml()+replanHtml(t)+metaHtml(t)+hintsHtml(t.sessionHints)+queueHtml(`reviewQ`,`reviewQ`,t.reviewQ)+queueHtml(`newQ`,`newQ`,t.newQ)+queueHtml(`failedQ`,`failedQ`,t.failedQ),overlayEl.querySelectorAll(`[data-q]`).forEach(e=>{e.onclick=()=>{let t=e.dataset.q;t&&(expanded[t]=!expanded[t],render())}})}function headerHtml(){return`<div style="font-weight:600;color:#93c5fd;margin-bottom:4px">⚙ SessionController</div>`}function replanHtml(e){return e.replanActive?`<div style="margin-bottom:6px;color:#fde047">${SPINNER_FRAMES[spinnerFrame%SPINNER_FRAMES.length]} replanning <span style="opacity:.85">[${esc(e.replanLabel??`(auto)`)}]</span></div>`:`<div style="margin-bottom:6px;opacity:.45">○ idle</div>`}function metaHtml(e){return`<div style="margin-bottom:6px">${[`time ${formatTime(e.secondsRemaining)}${e.hasCardGuarantee?` \xB7 <span style="color:#fbbf24">guarantee ${e.minCardsGuarantee}</span>`:``}`,`well-indicated left: ${e.wellIndicatedRemaining}`,`current: ${e.currentCard?esc(e.currentCard):`<span style="opacity:.6">—</span>`}`].map(e=>`<div>${e}</div>`).join(``)}</div>`}function hintsHtml(e){let t=[];return e&&(e.boostTags&&Object.keys(e.boostTags).length&&t.push(`boost: `+Object.entries(e.boostTags).map(([e,t])=>`${esc(e)}<span style="opacity:.6">\xD7${t}</span>`).join(`, `)),e.boostCards&&Object.keys(e.boostCards).length&&t.push(`boostCards: `+Object.entries(e.boostCards).map(([e,t])=>`${esc(e)}<span style="opacity:.6">\xD7${t}</span>`).join(`, `)),e.requireCards?.length&&t.push(`require: ${e.requireCards.map(esc).join(`, `)}`),e.requireTags?.length&&t.push(`requireTags: ${e.requireTags.map(esc).join(`, `)}`),e.excludeTags?.length&&t.push(`exclude: ${e.excludeTags.map(esc).join(`, `)}`),e.excludeCards?.length&&t.push(`excludeCards: ${e.excludeCards.map(esc).join(`, `)}`)),`<div style="margin-bottom:6px"><div style="color:#86efac">sessionHints</div>${t.length?t.map(e=>`<div style="margin-left:6px">${e}</div>`).join(``):`<div style="margin-left:6px;opacity:.6">none</div>`}</div>`}function queueHtml(e,t,n){let r=n.length>INLINE_THRESHOLD,a=!r||expanded[e],o=r?expanded[e]?`▾ `:`▸ `:``,s=n.dequeueCount?` <span style="opacity:.5">drawn ${n.dequeueCount}</span>`:``,c=r?`cursor:pointer;color:#f9a8d4`:`color:#f9a8d4`,l=`<div${r?` data-q="${e}"`:``} style="${c}">${o}${t}: ${n.length}${s}</div>`,u=``;return u=a&&n.cards.length?`<ol style="margin:2px 0 6px 0;padding-left:20px">`+n.cards.map(e=>`<li style="white-space:nowrap">${esc(e)}</li>`).join(``)+`</ol>`:n.cards.length?`<div style="margin:1px 0 6px 6px;opacity:.55">(${n.length} cards \u2014 click to expand)</div>`:`<div style="margin:1px 0 6px 6px;opacity:.5">empty</div>`,l+u}function formatTime(e){let t=Math.max(0,Math.round(e));return`${Math.floor(t/60)}:${(t%60).toString().padStart(2,`0`)}`}function esc(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`)}var activeSession=null,sessionHistory=[],MAX_HISTORY=5;function startSessionTracking(e,t,n){clearRunHistory();let r=`session-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;activeSession={sessionId:r,startTime:new Date,initialQueues:{timestamp:new Date,reviewQLength:e,newQLength:t,failedQLength:n},presentations:[],queueSnapshots:[]},logger.debug(`[SessionDebugger] Started tracking session: ${r}`)}function recordCardPresentation(e,t,n,r,a,o){if(!activeSession){logger.warn(`[SessionDebugger] No active session to record presentation`);return}activeSession.presentations.push({timestamp:new Date,sequenceNumber:activeSession.presentations.length+1,cardId:e,courseId:t,courseName:n,origin:r,queueSource:a,score:o})}function snapshotQueues(e,t,n,r,a){activeSession&&activeSession.queueSnapshots.push({timestamp:new Date,reviewQLength:e,newQLength:t,failedQLength:n,reviewQNext3:r,newQNext3:a})}function endSessionTracking(){activeSession&&(activeSession.endTime=new Date,sessionHistory.unshift(activeSession),sessionHistory.length>MAX_HISTORY&&sessionHistory.pop(),logger.debug(`[SessionDebugger] Ended session: ${activeSession.sessionId}`),activeSession=null)}function showCurrentQueue(){if(!activeSession){logger.info(`[Session Debug] No active session.`);return}let e=activeSession.queueSnapshots[activeSession.queueSnapshots.length-1]||activeSession.initialQueues;console.group(`📊 Current Queue State`),logger.info(`Review Queue: ${e.reviewQLength} cards`),e.reviewQNext3&&e.reviewQNext3.length>0&&logger.info(` Next: ${e.reviewQNext3.join(`, `)}`),logger.info(`New Queue: ${e.newQLength} cards`),e.newQNext3&&e.newQNext3.length>0&&logger.info(` Next: ${e.newQNext3.join(`, `)}`),logger.info(`Failed Queue: ${e.failedQLength} cards`),console.groupEnd()}function showPresentationHistory(e=0){let t=e===0&&activeSession?activeSession:sessionHistory[e];if(!t){logger.info(`[Session Debug] No session found at index ${e}`);return}console.group(`\u{1F4DC} Session History: ${t.sessionId}`),logger.info(`Started: ${t.startTime.toLocaleTimeString()}`),t.endTime&&logger.info(`Ended: ${t.endTime.toLocaleTimeString()}`),logger.info(`Cards presented: ${t.presentations.length}`),t.presentations.length>0&&console.table(t.presentations.map(e=>({"#":e.sequenceNumber,course:e.courseName||e.courseId.slice(0,8),origin:e.origin,queue:e.queueSource,score:e.score?.toFixed(3)||`-`,time:e.timestamp.toLocaleTimeString()}))),console.groupEnd()}function showInterleaving(e=0){let t=e===0&&activeSession?activeSession:sessionHistory[e];if(!t){logger.info(`[Session Debug] No session found at index ${e}`);return}console.group(`🔀 Interleaving Analysis`);let n=new Map,r=new Map;if(t.presentations.forEach(e=>{let t=e.courseName||e.courseId;n.set(t,(n.get(t)||0)+1),r.has(t)||r.set(t,{review:0,new:0,failed:0});let a=r.get(t);a[e.origin]++}),logger.info(`Course distribution:`),console.table(Array.from(n.entries()).map(([e,n])=>{let a=r.get(e);return{course:e,total:n,reviews:a.review,new:a.new,failed:a.failed,percentage:(n/t.presentations.length*100).toFixed(1)+`%`}})),t.presentations.length>0){logger.info(`
|
|
344
344
|
Presentation sequence (first 20):`);let e=t.presentations.slice(0,20).map((e,t)=>`${t+1}. ${e.courseName||e.courseId.slice(0,8)} (${e.origin})`).join(`
|
|
345
345
|
`);logger.info(e)}let a=0,o=1,s=t.presentations[0]?.courseId;for(let e=1;e<t.presentations.length;e++)t.presentations[e].courseId===s?(o++,a=Math.max(a,o)):(s=t.presentations[e].courseId,o=1);a>3&&(logger.info(`
|
|
346
|
-
\u26A0\uFE0F Detected clustering: max ${a} cards from same course in a row`),logger.info(`This suggests cards are sorted by score rather than round-robin by course.`)),console.groupEnd()}var sessionDebugAPI={get sessions(){return[...sessionHistory]},get active(){return activeSession},showQueue(){showCurrentQueue()},showHistory(e=0){showPresentationHistory(e)},showInterleaving(e=0){showInterleaving(e)},listSessions(){if(activeSession&&logger.info(`Active session: ${activeSession.sessionId} (${activeSession.presentations.length} cards presented)`),sessionHistory.length===0){logger.info(`[Session Debug] No completed sessions in history.`);return}console.table(sessionHistory.map((e,t)=>({index:t,id:e.sessionId.slice(-8),started:e.startTime.toLocaleTimeString(),ended:e.endTime?.toLocaleTimeString()||`incomplete`,cards:e.presentations.length})))},export(){let e={active:activeSession,history:sessionHistory},t=JSON.stringify(e,null,2);return logger.info(`[Session Debug] Session data exported. Copy the returned string or use:`),logger.info(` copy(window.skuilder.session.export())`),t},clear(){sessionHistory.length=0,logger.info(`[Session Debug] Session history cleared.`)},help(){logger.info(`
|
|
346
|
+
\u26A0\uFE0F Detected clustering: max ${a} cards from same course in a row`),logger.info(`This suggests cards are sorted by score rather than round-robin by course.`)),console.groupEnd()}var sessionDebugAPI={get sessions(){return[...sessionHistory]},get active(){return activeSession},showQueue(){showCurrentQueue()},dbgOverlay(){toggleSessionOverlay()},showHistory(e=0){showPresentationHistory(e)},showInterleaving(e=0){showInterleaving(e)},listSessions(){if(activeSession&&logger.info(`Active session: ${activeSession.sessionId} (${activeSession.presentations.length} cards presented)`),sessionHistory.length===0){logger.info(`[Session Debug] No completed sessions in history.`);return}console.table(sessionHistory.map((e,t)=>({index:t,id:e.sessionId.slice(-8),started:e.startTime.toLocaleTimeString(),ended:e.endTime?.toLocaleTimeString()||`incomplete`,cards:e.presentations.length})))},export(){let e={active:activeSession,history:sessionHistory},t=JSON.stringify(e,null,2);return logger.info(`[Session Debug] Session data exported. Copy the returned string or use:`),logger.info(` copy(window.skuilder.session.export())`),t},clear(){sessionHistory.length=0,logger.info(`[Session Debug] Session history cleared.`)},help(){logger.info(`
|
|
347
347
|
🎯 Session Debug API
|
|
348
348
|
|
|
349
349
|
Commands:
|
|
350
|
+
.dbgOverlay() Toggle the pinned live overlay (queues, hints, timer)
|
|
350
351
|
.showQueue() Show current queue state (active session only)
|
|
351
352
|
.showHistory(index?) Show presentation history (0=current/last, 1=previous, etc)
|
|
352
353
|
.showInterleaving(index?) Analyze course interleaving pattern
|
|
@@ -363,14 +364,14 @@ Example:
|
|
|
363
364
|
window.skuilder.session.showQueue()
|
|
364
365
|
`)}};function mountSessionDebugger(){if(typeof window>`u`)return;let e=window;e.skuilder=e.skuilder||{},e.skuilder.session=sessionDebugAPI}mountSessionDebugger(),init_logger();var SessionController=(_SessionController2=class _SessionController extends Loggable{set sessionRecord(e){this._sessionRecord=e}get secondsRemaining(){return this._secondsRemaining}get hasCardGuarantee(){return this._minCardsGuarantee>0}get report(){let e=this.reviewQ.dequeueCount,t=this.newQ.dequeueCount;return`${e} ${e===1?`review`:`reviews`}, ${t} ${t===1?`new card`:`new cards`}`}get detailedReport(){return this.newQ.toString+`
|
|
365
366
|
`+this.reviewQ.toString+`
|
|
366
|
-
`+this.failedQ.toString}constructor(e,t,n,r,a,o){super(),_defineProperty(this,`_className`,`SessionController`),_defineProperty(this,`services`,void 0),_defineProperty(this,`srsService`,void 0),_defineProperty(this,`eloService`,void 0),_defineProperty(this,`hydrationService`,void 0),_defineProperty(this,`mixer`,void 0),_defineProperty(this,`dataLayer`,void 0),_defineProperty(this,`courseNameCache`,new Map),_defineProperty(this,`_defaultBatchLimit`,20),_defineProperty(this,`_initialReviewCap`,200),_defineProperty(this,`sources`,void 0),_defineProperty(this,`_sessionRecord`,[]),_defineProperty(this,`_currentCard`,null),_defineProperty(this,`reviewQ`,new ItemQueue),_defineProperty(this,`newQ`,new ItemQueue),_defineProperty(this,`failedQ`,new ItemQueue),_defineProperty(this,`_replanPromise`,null),_defineProperty(this,`_wellIndicatedRemaining`,0),_defineProperty(this,`_suppressQualityReplan`,!1),_defineProperty(this,`_minCardsGuarantee`,0),_defineProperty(this,`_sessionHints`,null),_defineProperty(this,`_outcomeObservers`,[]),_defineProperty(this,`_sessionControls`,null),_defineProperty(this,`startTime`,void 0),_defineProperty(this,`endTime`,void 0),_defineProperty(this,`_secondsRemaining`,void 0),_defineProperty(this,`_intervalHandle`,void 0),this.dataLayer=n,this.mixer=a||new QuotaRoundRobinMixer,this.srsService=new SrsService(n.getUserDB()),this.eloService=new EloService(n,n.getUserDB()),this.hydrationService=new CardHydrationService(r,e=>n.getCourseDB(e),()=>this._getItemsToHydrate()),this.services={response:new ResponseProcessor(this.srsService,this.eloService)},this.sources=e,this.startTime=new Date,this._secondsRemaining=t,this.endTime=new Date(this.startTime.valueOf()+1e3*this._secondsRemaining),o?.defaultBatchLimit!==void 0&&(this._defaultBatchLimit=o.defaultBatchLimit),o?.initialReviewCap!==void 0&&(this._initialReviewCap=o.initialReviewCap),o?.outcomeObservers?.length&&(this._outcomeObservers=[...o.outcomeObservers]),this.log(`Session constructed:
|
|
367
|
+
`+this.failedQ.toString}constructor(e,t,n,r,a,o){super(),_defineProperty(this,`_className`,`SessionController`),_defineProperty(this,`services`,void 0),_defineProperty(this,`srsService`,void 0),_defineProperty(this,`eloService`,void 0),_defineProperty(this,`hydrationService`,void 0),_defineProperty(this,`mixer`,void 0),_defineProperty(this,`dataLayer`,void 0),_defineProperty(this,`courseNameCache`,new Map),_defineProperty(this,`_defaultBatchLimit`,20),_defineProperty(this,`_initialReviewCap`,200),_defineProperty(this,`sources`,void 0),_defineProperty(this,`_sessionRecord`,[]),_defineProperty(this,`_currentCard`,null),_defineProperty(this,`reviewQ`,new ItemQueue),_defineProperty(this,`newQ`,new ItemQueue),_defineProperty(this,`failedQ`,new ItemQueue),_defineProperty(this,`_replanPromise`,null),_defineProperty(this,`_activeReplanLabel`,null),_defineProperty(this,`_wellIndicatedRemaining`,0),_defineProperty(this,`_suppressQualityReplan`,!1),_defineProperty(this,`_minCardsGuarantee`,0),_defineProperty(this,`_sessionHints`,null),_defineProperty(this,`_outcomeObservers`,[]),_defineProperty(this,`_sessionControls`,null),_defineProperty(this,`startTime`,void 0),_defineProperty(this,`endTime`,void 0),_defineProperty(this,`_secondsRemaining`,void 0),_defineProperty(this,`_intervalHandle`,void 0),this.dataLayer=n,this.mixer=a||new QuotaRoundRobinMixer,this.srsService=new SrsService(n.getUserDB()),this.eloService=new EloService(n,n.getUserDB()),this.hydrationService=new CardHydrationService(r,e=>n.getCourseDB(e),()=>this._getItemsToHydrate()),this.services={response:new ResponseProcessor(this.srsService,this.eloService)},this.sources=e,this.startTime=new Date,this._secondsRemaining=t,this.endTime=new Date(this.startTime.valueOf()+1e3*this._secondsRemaining),o?.defaultBatchLimit!==void 0&&(this._defaultBatchLimit=o.defaultBatchLimit),o?.initialReviewCap!==void 0&&(this._initialReviewCap=o.initialReviewCap),o?.outcomeObservers?.length&&(this._outcomeObservers=[...o.outcomeObservers]),this.log(`Session constructed:
|
|
367
368
|
startTime: ${this.startTime}
|
|
368
369
|
endTime: ${this.endTime}
|
|
369
370
|
defaultBatchLimit: ${this._defaultBatchLimit}
|
|
370
|
-
initialReviewCap: ${this._initialReviewCap}`)}tick(){this._secondsRemaining=Math.floor((this.endTime.valueOf()-Date.now())/1e3),this._secondsRemaining<=0&&clearInterval(this._intervalHandle)}estimateCleanupTime(){let e=0;for(let t=0;t<this.failedQ.length;t++){let n=this.failedQ.peek(t),r=this._sessionRecord.find(e=>e.item.cardID===n.cardID),a=0;if(r){for(let e=0;e<r.records.length;e++)a+=r.records[e].timeSpent;a/=r.records.length,e+=a}}let t=e/1e3;return this.log(`Failed card cleanup estimate: ${Math.round(t)}`),t}estimateReviewTime(){let e=5*this.reviewQ.length;return this.log(`Review card time estimate: ${e}`),e}async prepareSession(){if(this.sources.some(e=>typeof e.getWeightedCards!=`function`))throw Error(`[SessionController] All content sources must implement getWeightedCards().`);let e=await this.getWeightedContent();this._wellIndicatedRemaining=e,e>=0&&e<_SessionController.MIN_WELL_INDICATED&&this.log(`[Init] Only ${e}/${_SessionController.MIN_WELL_INDICATED} well-indicated cards in initial load`),await this.hydrationService.ensureHydratedCards(),startSessionTracking(this.reviewQ.length,this.newQ.length,this.failedQ.length),this._intervalHandle=setInterval(()=>{this.tick()},1e3)}async requestReplan(e){let t=this.normalizeReplanOptions(e),n=this._replanHasIntent(t);if(this._replanPromise){if(!n)return this.log(`Replan already in progress, coalescing unhinted auto-replan`),this._replanPromise;let e=t.label?` [${t.label}]`:``;this.log(`Replan in progress; queueing hint-bearing replan${e} behind in-flight run`);let r=this._replanPromise.catch(()=>void 0).then(()=>this._runReplan(t))
|
|
371
|
+
initialReviewCap: ${this._initialReviewCap}`),registerActiveController(this)}tick(){this._secondsRemaining=Math.floor((this.endTime.valueOf()-Date.now())/1e3),this._secondsRemaining<=0&&clearInterval(this._intervalHandle)}estimateCleanupTime(){let e=0;for(let t=0;t<this.failedQ.length;t++){let n=this.failedQ.peek(t),r=this._sessionRecord.find(e=>e.item.cardID===n.cardID),a=0;if(r){for(let e=0;e<r.records.length;e++)a+=r.records[e].timeSpent;a/=r.records.length,e+=a}}let t=e/1e3;return this.log(`Failed card cleanup estimate: ${Math.round(t)}`),t}estimateReviewTime(){let e=5*this.reviewQ.length;return this.log(`Review card time estimate: ${e}`),e}async prepareSession(){if(this.sources.some(e=>typeof e.getWeightedCards!=`function`))throw Error(`[SessionController] All content sources must implement getWeightedCards().`);let e=await this.getWeightedContent();this._wellIndicatedRemaining=e,e>=0&&e<_SessionController.MIN_WELL_INDICATED&&this.log(`[Init] Only ${e}/${_SessionController.MIN_WELL_INDICATED} well-indicated cards in initial load`),await this.hydrationService.ensureHydratedCards(),startSessionTracking(this.reviewQ.length,this.newQ.length,this.failedQ.length),this._intervalHandle=setInterval(()=>{this.tick()},1e3)}async requestReplan(e){let t=this.normalizeReplanOptions(e),n=this._replanHasIntent(t);if(this._replanPromise){if(!n)return this.log(`Replan already in progress, coalescing unhinted auto-replan`),this._replanPromise;let e=t.label?` [${t.label}]`:``;this.log(`Replan in progress; queueing hint-bearing replan${e} behind in-flight run`);let r=this._replanPromise.catch(()=>void 0).then(()=>this._runReplan(t)),a=r.finally(()=>{this._replanPromise===a&&(this._replanPromise=null,this._activeReplanLabel=null)});return this._replanPromise=a,r}let r=this._runReplan(t),a=r.finally(()=>{this._replanPromise===a&&(this._replanPromise=null,this._activeReplanLabel=null)});this._replanPromise=a,await r}_replanHasIntent(e){return!!(e.limit!==void 0||e.minFollowUpCards!==void 0||e.mode&&e.mode!==`replace`||e.hints&&Object.keys(e.hints).length>0||e.sessionHints!==void 0)}async _runReplan(e){this._activeReplanLabel=e.label??`(auto)`,e.hints||(e.hints={});let t=e.hints,n=new Set(t.excludeCards??[]);this._currentCard?.item.cardID&&n.add(this._currentCard.item.cardID);for(let e of this._sessionRecord)n.add(e.card.card_id);this.newQ.length>0&&n.add(this.newQ.peek(0).cardID),t.excludeCards=[...n],e.sessionHints!==void 0&&(this._sessionHints=e.sessionHints,this.log(`[Replan] Session hints ${e.sessionHints?`set`:`cleared`}: ${JSON.stringify(e.sessionHints)}`)),this._applyHintsToSources(e.hints,e.label);let r=e.label?` [${e.label}]`:``;this.log(`Mid-session replan requested${r} (limit: ${e.limit??`default`}, mode: ${e.mode??`replace`}${e.hints?`, with hints`:``})`),e.minFollowUpCards!==void 0&&e.minFollowUpCards>0&&(this._minCardsGuarantee=Math.max(this._minCardsGuarantee,e.minFollowUpCards),this.log(`[Replan] Card guarantee set to ${this._minCardsGuarantee}`)),await this._executeReplan(e)}setSessionHints(e){this._sessionHints=e,this.log(`Session hints ${e?`set`:`cleared`}: ${JSON.stringify(e)}`)}getSessionHints(){return this._sessionHints}getDebugSnapshot(){let describe=e=>{let t=[];for(let n=0;n<e.length;n++)t.push(e.peek(n).cardID);return{length:e.length,dequeueCount:e.dequeueCount,cards:t}};return{secondsRemaining:this.secondsRemaining,hasCardGuarantee:this.hasCardGuarantee,minCardsGuarantee:this._minCardsGuarantee,wellIndicatedRemaining:this._wellIndicatedRemaining,currentCard:this._currentCard?.item.cardID??null,sessionHints:this._sessionHints,replanActive:this._replanPromise!==null,replanLabel:this._activeReplanLabel,reviewQ:describe(this.reviewQ),newQ:describe(this.newQ),failedQ:describe(this.failedQ)}}mergeSessionHints(e){this._sessionHints=mergeHints2([this._sessionHints,e])??null,this.log(`Session hints merged: ${JSON.stringify(this._sessionHints)}`)}_applyHintsToSources(e,t){let n=e&&t?{...e,_label:t}:e,r=mergeHints2([this._sessionHints,n]);if(r)for(let e of this.sources)e.setEphemeralHints?.(r)}_getSessionControls(){return this._sessionControls||(this._sessionControls={getSessionHints:()=>this.getSessionHints(),setSessionHints:e=>this.setSessionHints(e),mergeSessionHints:e=>this.mergeSessionHints(e),requestReplan:e=>this.requestReplan(e)}),this._sessionControls}async _notifyOutcomeObservers(e,t,n){if(this._outcomeObservers.length===0||!isQuestionRecord(e))return;let r={record:e,card:t.card,result:n},a=this._getSessionControls();for(let e of this._outcomeObservers)try{await e(r,a)}catch(e){this.error(`[OutcomeObserver] observer threw; ignoring`,e)}}async _replanUncoalesced(e){let t=this._runReplan(e),n=t.finally(()=>{this._replanPromise===n&&(this._replanPromise=null,this._activeReplanLabel=null)});this._replanPromise=n,await t}normalizeReplanOptions(e){if(!e)return{};let t=[`hints`,`sessionHints`,`limit`,`mode`,`label`,`minFollowUpCards`];return Object.keys(e).some(e=>t.includes(e))?e:{hints:e}}async _executeReplan(e={}){let t=e.limit,n=e.mode??`replace`,r=await this.getWeightedContent({replan:!0,additive:n===`merge`,limit:t});this._wellIndicatedRemaining=r,t!==void 0&&t<this._defaultBatchLimit?(this._suppressQualityReplan=!0,this.log(`[Replan] Burst mode (limit=${t}): suppressing quality-based auto-replan`)):this._suppressQualityReplan=!1,r>=0&&r<_SessionController.MIN_WELL_INDICATED&&this.log(`[Replan] Only ${r}/${_SessionController.MIN_WELL_INDICATED} well-indicated cards after replan`),await this.hydrationService.ensureHydratedCards();let a=e.label?` [${e.label}]`:``;this.log(`Replan complete${a}: newQ now has ${this.newQ.length} cards (mode=${n})`),snapshotQueues(this.reviewQ.length,this.newQ.length,this.failedQ.length)}addTime(e){this.endTime=new Date(this.endTime.valueOf()+1e3*e)}get failedCount(){return this.failedQ.length}toString(){return`Session: ${this.reviewQ.length} Reviews, ${this.newQ.length} New, ${this.failedQ.length} failed`}reportString(){return`${this.reviewQ.dequeueCount} Reviews, ${this.newQ.dequeueCount} New, ${this.failedQ.dequeueCount} failed`}getDebugInfo(){let e=this.sources.some(e=>typeof e.getWeightedCards==`function`),extractQueueItems=(e,t=10)=>{let n=[];for(let r=0;r<Math.min(e.length,t);r++){let t=e.peek(r);n.push({courseID:t.courseID||`unknown`,cardID:t.cardID||`unknown`,status:t.status||`unknown`})}return n};return{api:{mode:e?`weighted`:`legacy`,description:e?`Using getWeightedCards() API with scored candidates`:`ERROR: getWeightedCards() not a function.`},reviewQueue:{length:this.reviewQ.length,dequeueCount:this.reviewQ.dequeueCount,items:extractQueueItems(this.reviewQ)},newQueue:{length:this.newQ.length,dequeueCount:this.newQ.dequeueCount,items:extractQueueItems(this.newQ)},failedQueue:{length:this.failedQ.length,dequeueCount:this.failedQ.dequeueCount,items:extractQueueItems(this.failedQ)},hydratedCache:{count:this.hydrationService.hydratedCount,cardIds:this.hydrationService.getHydratedCardIds()},replan:{inProgress:this._replanPromise!==null,suppressQualityReplan:this._suppressQualityReplan,defaultBatchLimit:this._defaultBatchLimit,minCardsGuarantee:this._minCardsGuarantee}}}async getWeightedContent(e){let t=e?.replan??!1,n=e?.additive??!1,r=e?.limit??this._defaultBatchLimit,a=t?r:r+this._initialReviewCap;t||this._applyHintsToSources();let o=[];for(let e=0;e<this.sources.length;e++){let t=this.sources[e];try{let n=(await t.getWeightedCards(a)).cards;o.push({sourceIndex:e,weighted:n})}catch(t){if(this.error(`Failed to get content from source ${e}:`,t),this.sources.length===1)throw Error(`Cannot start session: failed to load content from source ${e}`)}}if(o.length===0){if(t)return this.log(`Replan: no content from any source, keeping existing newQ`),-1;throw Error(`Cannot start session: failed to load content from all ${this.sources.length} source(s). Check logs for details.`)}let s=this.mixer.mix(o,a*this.sources.length),c=o.map(e=>e.weighted[0]?.courseId||`source-${e.sourceIndex}`);await Promise.all(c.map(async e=>{if(!this.courseNameCache.has(e))try{let t=await this.dataLayer.getCoursesDB().getCourseConfig(e);this.courseNameCache.set(e,t.name)}catch{}}));let l=c.map(e=>this.courseNameCache.get(e)),u=this.mixer instanceof QuotaRoundRobinMixer?Math.ceil(a*this.sources.length/o.length):void 0;captureMixerRun(this.mixer.constructor.name,o,c,l,a*this.sources.length,u,s);let d=s.filter(e=>getCardOrigin(e)===`review`).slice(0,this._initialReviewCap),p=s.filter(e=>getCardOrigin(e)===`new`).slice(0,r);logger.debug(`[reviews] got ${d.length} reviews from mixer`);let m=t?`Replan content:
|
|
371
372
|
`:`Mixed content session created with:
|
|
372
373
|
`;if(!t)for(let e of d){let t={cardID:e.cardId,courseID:e.courseId,contentSourceType:`course`,contentSourceID:e.courseId,reviewID:e.reviewID,status:`review`};this.reviewQ.add(t,t.cardID),m+=`Review: ${e.courseId}::${e.cardId} (score: ${e.score.toFixed(2)})
|
|
373
374
|
`}let g=p.filter(e=>e.score>=_SessionController.WELL_INDICATED_SCORE).length,_=[];for(let e of p){let t={cardID:e.cardId,courseID:e.courseId,contentSourceType:`course`,contentSourceID:e.courseId,status:`new`};_.push(t),m+=`New: ${e.courseId}::${e.cardId} (score: ${e.score.toFixed(2)})
|
|
374
375
|
`}if(n){let e=this.newQ.mergeToFront(_,e=>e.cardID);m+=`Additive merge: ${e} new cards added to front of newQ
|
|
375
|
-
`}else if(t)this.newQ.replaceAll(_,e=>e.cardID);else for(let e of _)this.newQ.add(e,e.cardID);return this.log(m),g}_getItemsToHydrate(){let e=[],t=2;for(let t=0;t<Math.min(2,this.reviewQ.length);t++)e.push(this.reviewQ.peek(t));for(let t=0;t<Math.min(2,this.newQ.length);t++)e.push(this.newQ.peek(t));for(let t=0;t<Math.min(2,this.failedQ.length);t++)e.push(this.failedQ.peek(t));return e}_selectNextItemToHydrate(){let e=Math.random(),t=.1,n=.75;if(this.reviewQ.length===0&&this.failedQ.length===0&&this.newQ.length===0||this._secondsRemaining<2&&this.failedQ.length===0&&this._minCardsGuarantee<=0)return null;if(this._secondsRemaining<=0&&this._minCardsGuarantee<=0)return this.failedQ.length>0?this.failedQ.peek(0):null;if(this.newQ.dequeueCount<this.sources.length&&this.newQ.length)return this.newQ.peek(0);let r=this.estimateCleanupTime(),a=this.estimateReviewTime();return this._secondsRemaining-(r+a)>20?(t=.5,n=.9):this._secondsRemaining-r>20?(t=.05,n=.9):(t=.01,n=.1),this.failedQ.length===0&&(n=1),this.reviewQ.length===0&&(t=n),e<t&&this.newQ.length?this.newQ.peek(0):e<n&&this.reviewQ.length?this.reviewQ.peek(0):this.failedQ.length?this.failedQ.peek(0):(this.log(`No more cards available for the session!`),null)}async nextCard(e=`dismiss-success`){if(this.dismissCurrentCard(e),this._minCardsGuarantee>0&&(this._minCardsGuarantee--,this.log(`[CardGuarantee] ${this._minCardsGuarantee} guaranteed cards remaining`)),this._replanPromise&&this.newQ.length===0&&this.reviewQ.length===0&&this.failedQ.length===0&&(this.log(`nextCard: queues empty, awaiting in-flight replan before drawing`),await this._replanPromise),this.newQ.length<=_SessionController.DEPLETION_PREFETCH_THRESHOLD&&this._secondsRemaining>0&&!this._replanPromise){this._suppressQualityReplan=!1;let e=this.reviewQ.length+this.failedQ.length;this.log(`[AutoReplan:depletion] newQ has ${this.newQ.length} card(s) (${e} in other queues) with ${this._secondsRemaining}s remaining. Triggering background replan.`),this.requestReplan()}if(!this._suppressQualityReplan&&this._wellIndicatedRemaining<=3&&this.newQ.length>0&&!this._replanPromise&&(this.log(`[AutoReplan:quality] ${this._wellIndicatedRemaining} well-indicated cards remaining (newQ: ${this.newQ.length}). Triggering background replan.`),this.requestReplan()),this._secondsRemaining<=0&&this.failedQ.length===0&&this._minCardsGuarantee<=0)return this._currentCard=null,endSessionTracking(),null;let t=3,n=250,r=0;for(;this._secondsRemaining>0&&this.newQ.length===0&&this.reviewQ.length===0&&this.failedQ.length===0;)if(this.log(`[WedgeBreaker] All queues empty with ${this._secondsRemaining}s remaining. Running pipeline (attempt ${r+1}/3).`),await this._replanUncoalesced({label:`wedge-breaker`}),this.newQ.length===0&&this.reviewQ.length===0&&this.failedQ.length===0){if(r++,r>=3){this.log(`[WedgeBreaker] Pipeline returned no content 3 consecutive times. Giving up; session will end.`);break}await new Promise(e=>setTimeout(e,250))}else r=0;let a=20;for(let e=0;e<20;e++){let e=this._selectNextItemToHydrate();if(!e)return this._currentCard=null,endSessionTracking(),null;let t=this.hydrationService.getHydratedCard(e.cardID);if(t||(t=await this.hydrationService.waitForCard(e.cardID)),this.removeItemFromQueue(e),t){await this.hydrationService.ensureHydratedCards(),this._currentCard=t;let n=e.status===`review`||e.status===`failed-review`?`review`:e.status===`new`||e.status===`failed-new`?`new`:`failed`,r=e.status.startsWith(`failed`)?`failedQ`:e.status===`review`?`reviewQ`:`newQ`;return recordCardPresentation(e.cardID,e.courseID,this.courseNameCache.get(e.courseID),n,r),snapshotQueues(this.reviewQ.length,this.newQ.length,this.failedQ.length),t}this.log(`Skipping card ${e.cardID}: hydration failed, trying next`),isReview(e)&&this.srsService.removeReview(e.reviewID)}return this.log(`Exhausted 20 skip attempts finding a hydratable card`),this._currentCard=null,endSessionTracking(),null}async submitResponse(e,t,n,r,a,o,s,c,l){let u={...r.item},d=await this.services.response.processResponse(e,t,u,n,r,a,o,s,c,l);return await this._notifyOutcomeObservers(e,r,d),d}dismissCurrentCard(e=`dismiss-success`){if(this._currentCard)if(e===`dismiss-success`)this.hydrationService.removeCard(this._currentCard.item.cardID);else if(e===`marked-failed`){let e;e=isReview(this._currentCard.item)?{cardID:this._currentCard.item.cardID,courseID:this._currentCard.item.courseID,contentSourceID:this._currentCard.item.contentSourceID,contentSourceType:this._currentCard.item.contentSourceType,status:`failed-review`,reviewID:this._currentCard.item.reviewID}:{cardID:this._currentCard.item.cardID,courseID:this._currentCard.item.courseID,contentSourceID:this._currentCard.item.contentSourceID,contentSourceType:this._currentCard.item.contentSourceType,status:`failed-new`},this.failedQ.add(e,e.cardID)}else (e===`dismiss-error`||e===`dismiss-failed`)&&this.hydrationService.removeCard(this._currentCard.item.cardID)}removeItemFromQueue(e){this.reviewQ.peek(0)?.cardID===e.cardID?this.reviewQ.dequeue(e=>e.cardID):this.newQ.peek(0)?.cardID===e.cardID?(this.newQ.dequeue(e=>e.cardID),this._wellIndicatedRemaining>0&&this._wellIndicatedRemaining--):this.failedQ.peek(0)?.cardID===e.cardID&&this.failedQ.dequeue(e=>e.cardID)}async endSession(){if(!this._sessionRecord||this._sessionRecord.length===0)return;let e=this._sessionRecord.flatMap(e=>e.records).filter(e=>e.userAnswer!==void 0);if(e.length===0)return;let t=null,n=[];for(let e of this.sources)if(e.getOrchestrationContext){try{t=await e.getOrchestrationContext(),e.getStrategyIds&&n.push(...e.getStrategyIds())}catch(e){logger.warn(`[SessionController] Failed to get orchestration context: ${e}`)}if(t)break}if(!t){logger.debug(`[SessionController] No orchestration context available, skipping outcome recording`);return}let r=new Date().toISOString(),a=new Date(this.startTime).toISOString();await recordUserOutcome(t,a,r,e,n)}},_defineProperty(_SessionController2,`MIN_WELL_INDICATED`,5),_defineProperty(_SessionController2,`WELL_INDICATED_SCORE`,.1),_defineProperty(_SessionController2,`DEPLETION_PREFETCH_THRESHOLD`,3),_SessionController2);init_TagFilteredContentSource(),init_factory();export{processCustomQuestionsData as $,getDbPath as A,isFilter as B,createOrchestrationContext as C,getCardHistoryID as D,getAppDataDirectory as E,hasRegisteredNavigator as F,log as G,isQuestionRecord as H,importParsedCards as I,mountPipelineDebugger as J,mixerDebugAPI as K,initializeDataDirectory as L,getRegisteredNavigatorNames as M,getRegisteredNavigatorRole as N,getCardOrigin as O,getStudySource as P,pipelineDebugAPI as Q,initializeDataLayer as R,computeSpread as S,ensureAppDataDirectory as T,isQuestionTypeRegistered as U,isGenerator as V,isReview as W,mountUserDBDebugger as X,mountSessionDebugger as Y,newInterval as Z,buildStrategyStateId as _,validateStaticCourse as _t,ENV as a,registerSeedData as at,computeEffectiveWeight as b,Loggable as c,scoreAccuracyInZone as ct,NavigatorRoles as d,startSessionTracking as dt,recordCardPresentation as et,Navigators as f,updateLearningState as ft,areQuestionRecords as g,validateProcessorConfig as gt,TagFilteredContentSource as h,validateMigration as ht,DocTypePrefixes as i,registerQuestionType as it,getRegisteredNavigator as j,getDataLayer as k,NOT_SET as l,sessionDebugAPI as lt,SessionController as m,userDBDebugAPI as mt,CourseLookup as n,registerDataShape as nt,FileSystemError as o,removeDataShape as ot,QuotaRoundRobinMixer as p,updateStrategyWeight as pt,mountMixerDebugger as q,DocType as r,registerNavigator as rt,GuestUsername as s,removeQuestionType as st,ContentNavigator as t,recordUserOutcome as tt,NavigatorRole as u,snapshotQueues as ut,captureMixerRun as v,endSessionTracking as w,computeOutcomeSignal as x,computeDeviation as y,initializeNavigatorRegistry as z};
|
|
376
|
-
//# sourceMappingURL=dist-
|
|
376
|
+
`}else if(t)this.newQ.replaceAll(_,e=>e.cardID);else for(let e of _)this.newQ.add(e,e.cardID);return this.log(m),g}_getItemsToHydrate(){let e=[],t=2;for(let t=0;t<Math.min(2,this.reviewQ.length);t++)e.push(this.reviewQ.peek(t));for(let t=0;t<Math.min(2,this.newQ.length);t++)e.push(this.newQ.peek(t));for(let t=0;t<Math.min(2,this.failedQ.length);t++)e.push(this.failedQ.peek(t));return e}_selectNextItemToHydrate(){let e=Math.random(),t=.1,n=.75;if(this.reviewQ.length===0&&this.failedQ.length===0&&this.newQ.length===0||this._secondsRemaining<2&&this.failedQ.length===0&&this._minCardsGuarantee<=0)return null;if(this._secondsRemaining<=0&&this._minCardsGuarantee<=0)return this.failedQ.length>0?this.failedQ.peek(0):null;if(this.newQ.dequeueCount<this.sources.length&&this.newQ.length)return this.newQ.peek(0);let r=this.estimateCleanupTime(),a=this.estimateReviewTime();return this._secondsRemaining-(r+a)>20?(t=.5,n=.9):this._secondsRemaining-r>20?(t=.05,n=.9):(t=.01,n=.1),this.failedQ.length===0&&(n=1),this.reviewQ.length===0&&(t=n),e<t&&this.newQ.length?this.newQ.peek(0):e<n&&this.reviewQ.length?this.reviewQ.peek(0):this.failedQ.length?this.failedQ.peek(0):(this.log(`No more cards available for the session!`),null)}async nextCard(e=`dismiss-success`){if(this.dismissCurrentCard(e),this._minCardsGuarantee>0&&(this._minCardsGuarantee--,this.log(`[CardGuarantee] ${this._minCardsGuarantee} guaranteed cards remaining`)),this._replanPromise&&this.newQ.length===0&&this.reviewQ.length===0&&this.failedQ.length===0&&(this.log(`nextCard: queues empty, awaiting in-flight replan before drawing`),await this._replanPromise),this.newQ.length<=_SessionController.DEPLETION_PREFETCH_THRESHOLD&&this._secondsRemaining>0&&!this._replanPromise){this._suppressQualityReplan=!1;let e=this.reviewQ.length+this.failedQ.length;this.log(`[AutoReplan:depletion] newQ has ${this.newQ.length} card(s) (${e} in other queues) with ${this._secondsRemaining}s remaining. Triggering background replan.`),this.requestReplan({label:`auto:depletion`})}if(!this._suppressQualityReplan&&this._wellIndicatedRemaining<=3&&this.newQ.length>0&&!this._replanPromise&&(this.log(`[AutoReplan:quality] ${this._wellIndicatedRemaining} well-indicated cards remaining (newQ: ${this.newQ.length}). Triggering background replan.`),this.requestReplan({label:`auto:quality`})),this._secondsRemaining<=0&&this.failedQ.length===0&&this._minCardsGuarantee<=0)return this._currentCard=null,endSessionTracking(),null;let t=3,n=250,r=0;for(;this._secondsRemaining>0&&this.newQ.length===0&&this.reviewQ.length===0&&this.failedQ.length===0;)if(this.log(`[WedgeBreaker] All queues empty with ${this._secondsRemaining}s remaining. Running pipeline (attempt ${r+1}/3).`),await this._replanUncoalesced({label:`wedge-breaker`}),this.newQ.length===0&&this.reviewQ.length===0&&this.failedQ.length===0){if(r++,r>=3){this.log(`[WedgeBreaker] Pipeline returned no content 3 consecutive times. Giving up; session will end.`);break}await new Promise(e=>setTimeout(e,250))}else r=0;let a=20;for(let e=0;e<20;e++){let e=this._selectNextItemToHydrate();if(!e)return this._currentCard=null,endSessionTracking(),null;let t=this.hydrationService.getHydratedCard(e.cardID);if(t||(t=await this.hydrationService.waitForCard(e.cardID)),this.removeItemFromQueue(e),t){await this.hydrationService.ensureHydratedCards(),this._currentCard=t;let n=e.status===`review`||e.status===`failed-review`?`review`:e.status===`new`||e.status===`failed-new`?`new`:`failed`,r=e.status.startsWith(`failed`)?`failedQ`:e.status===`review`?`reviewQ`:`newQ`;return recordCardPresentation(e.cardID,e.courseID,this.courseNameCache.get(e.courseID),n,r),snapshotQueues(this.reviewQ.length,this.newQ.length,this.failedQ.length),t}this.log(`Skipping card ${e.cardID}: hydration failed, trying next`),isReview(e)&&this.srsService.removeReview(e.reviewID)}return this.log(`Exhausted 20 skip attempts finding a hydratable card`),this._currentCard=null,endSessionTracking(),null}async submitResponse(e,t,n,r,a,o,s,c,l){let u={...r.item},d=await this.services.response.processResponse(e,t,u,n,r,a,o,s,c,l);return await this._notifyOutcomeObservers(e,r,d),d}dismissCurrentCard(e=`dismiss-success`){if(this._currentCard)if(e===`dismiss-success`)this.hydrationService.removeCard(this._currentCard.item.cardID);else if(e===`marked-failed`){let e;e=isReview(this._currentCard.item)?{cardID:this._currentCard.item.cardID,courseID:this._currentCard.item.courseID,contentSourceID:this._currentCard.item.contentSourceID,contentSourceType:this._currentCard.item.contentSourceType,status:`failed-review`,reviewID:this._currentCard.item.reviewID}:{cardID:this._currentCard.item.cardID,courseID:this._currentCard.item.courseID,contentSourceID:this._currentCard.item.contentSourceID,contentSourceType:this._currentCard.item.contentSourceType,status:`failed-new`},this.failedQ.add(e,e.cardID)}else (e===`dismiss-error`||e===`dismiss-failed`)&&this.hydrationService.removeCard(this._currentCard.item.cardID)}removeItemFromQueue(e){this.reviewQ.peek(0)?.cardID===e.cardID?this.reviewQ.dequeue(e=>e.cardID):this.newQ.peek(0)?.cardID===e.cardID?(this.newQ.dequeue(e=>e.cardID),this._wellIndicatedRemaining>0&&this._wellIndicatedRemaining--):this.failedQ.peek(0)?.cardID===e.cardID&&this.failedQ.dequeue(e=>e.cardID)}async endSession(){if(!this._sessionRecord||this._sessionRecord.length===0)return;let e=this._sessionRecord.flatMap(e=>e.records).filter(e=>e.userAnswer!==void 0);if(e.length===0)return;let t=null,n=[];for(let e of this.sources)if(e.getOrchestrationContext){try{t=await e.getOrchestrationContext(),e.getStrategyIds&&n.push(...e.getStrategyIds())}catch(e){logger.warn(`[SessionController] Failed to get orchestration context: ${e}`)}if(t)break}if(!t){logger.debug(`[SessionController] No orchestration context available, skipping outcome recording`);return}let r=new Date().toISOString(),a=new Date(this.startTime).toISOString();await recordUserOutcome(t,a,r,e,n)}},_defineProperty(_SessionController2,`MIN_WELL_INDICATED`,5),_defineProperty(_SessionController2,`WELL_INDICATED_SCORE`,.1),_defineProperty(_SessionController2,`DEPLETION_PREFETCH_THRESHOLD`,3),_SessionController2);init_TagFilteredContentSource(),init_factory();export{processCustomQuestionsData as $,getDbPath as A,isFilter as B,createOrchestrationContext as C,getCardHistoryID as D,getAppDataDirectory as E,hasRegisteredNavigator as F,log as G,isQuestionRecord as H,importParsedCards as I,mountPipelineDebugger as J,mixerDebugAPI as K,initializeDataDirectory as L,getRegisteredNavigatorNames as M,getRegisteredNavigatorRole as N,getCardOrigin as O,getStudySource as P,pipelineDebugAPI as Q,initializeDataLayer as R,computeSpread as S,ensureAppDataDirectory as T,isQuestionTypeRegistered as U,isGenerator as V,isReview as W,mountUserDBDebugger as X,mountSessionDebugger as Y,newInterval as Z,buildStrategyStateId as _,validateStaticCourse as _t,ENV as a,registerSeedData as at,computeEffectiveWeight as b,Loggable as c,scoreAccuracyInZone as ct,NavigatorRoles as d,startSessionTracking as dt,recordCardPresentation as et,Navigators as f,updateLearningState as ft,areQuestionRecords as g,validateProcessorConfig as gt,TagFilteredContentSource as h,validateMigration as ht,DocTypePrefixes as i,registerQuestionType as it,getRegisteredNavigator as j,getDataLayer as k,NOT_SET as l,sessionDebugAPI as lt,SessionController as m,userDBDebugAPI as mt,CourseLookup as n,registerDataShape as nt,FileSystemError as o,removeDataShape as ot,QuotaRoundRobinMixer as p,updateStrategyWeight as pt,mountMixerDebugger as q,DocType as r,registerNavigator as rt,GuestUsername as s,removeQuestionType as st,ContentNavigator as t,recordUserOutcome as tt,NavigatorRole as u,snapshotQueues as ut,captureMixerRun as v,endSessionTracking as w,computeOutcomeSignal as x,computeDeviation as y,initializeNavigatorRegistry as z};
|
|
377
|
+
//# sourceMappingURL=dist-BrQ2uZNM.js.map
|