@vue-skuilder/standalone-ui 0.2.4 → 0.2.7

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.
@@ -11790,7 +11790,7 @@ var activePinia, setActivePinia, piniaSymbol, MutationType, IS_CLIENT, _global,
11790
11790
  init_lib(), setActivePinia = (t) => activePinia = t, piniaSymbol = process.env.NODE_ENV === "production" ? Symbol() : Symbol("pinia"), __name$2(isPlainObject$2, "isPlainObject"), (function(t) {
11791
11791
  t.direct = "direct", t.patchObject = "patch object", t.patchFunction = "patch function";
11792
11792
  })(MutationType || (MutationType = {})), IS_CLIENT = typeof window < "u", _global = typeof window == "object" && window.window === window ? window : typeof self == "object" && self.self === self ? self : typeof window == "object" && window.global === window ? window : typeof globalThis == "object" ? globalThis : { HTMLElement: null }, _navigator = typeof navigator == "object" ? navigator : { userAgent: "" }, isMacOSWebView = /Macintosh/.test(_navigator.userAgent) && /AppleWebKit/.test(_navigator.userAgent) && !/Safari/.test(_navigator.userAgent), saveAs = IS_CLIENT ? typeof HTMLAnchorElement < "u" && "download" in HTMLAnchorElement.prototype && !isMacOSWebView ? downloadSaveAs : "msSaveOrOpenBlob" in _navigator ? msSaveAs : fileSaverSaveAs : () => {}, {assign: assign$1} = Object, noop = () => {}, fallbackRunWithContext = (t) => t(), ACTION_MARKER = Symbol(), ACTION_NAME = Symbol(), skipHydrateSymbol = process.env.NODE_ENV === "production" ? Symbol() : Symbol("pinia:skipHydration"), {assign} = Object;
11793
- })), MarkdownRenderer_DoVbFpA6_exports = /* @__PURE__ */ __exportAll$1({
11793
+ })), MarkdownRenderer_kStoDRNE_exports = /* @__PURE__ */ __exportAll$1({
11794
11794
  C: () => I$2,
11795
11795
  E: () => __name,
11796
11796
  S: () => useViewable,
@@ -12036,9 +12036,12 @@ function splitByDelimiters(t, c, u) {
12036
12036
  else return [t];
12037
12037
  return d;
12038
12038
  }
12039
+ function stripTrailingNewlines(t) {
12040
+ return t.replace(/[\r\n]+$/, "");
12041
+ }
12039
12042
  function splitTextToken(t) {
12040
12043
  if (containsComponent(t)) {
12041
- let c = splitByDelimiters(t.text, "{{", "}}"), u = splitByDelimiters(t.raw, "{{", "}}");
12044
+ let c = splitByDelimiters(stripTrailingNewlines(t.text), "{{", "}}"), u = splitByDelimiters(stripTrailingNewlines(t.raw), "{{", "}}");
12042
12045
  if (c.length === u.length) return c.map((t, d) => ({
12043
12046
  type: "text",
12044
12047
  text: c[d],
@@ -12050,7 +12053,7 @@ function splitTextToken(t) {
12050
12053
  function splitParagraphToken(t) {
12051
12054
  let c = [];
12052
12055
  if (containsComponent(t)) {
12053
- let u = splitByDelimiters(t.text, "{{", "}}"), d = splitByDelimiters(t.raw, "{{", "}}");
12056
+ let u = splitByDelimiters(stripTrailingNewlines(t.text), "{{", "}}"), d = splitByDelimiters(stripTrailingNewlines(t.raw), "{{", "}}");
12054
12057
  if (u.length === d.length) {
12055
12058
  for (let t = 0; t < u.length; t++) {
12056
12059
  let m = {
@@ -13793,7 +13796,7 @@ function _sfc_render$29(t, c, u, d, m, g) {
13793
13796
  src: c.audio
13794
13797
  }, null, 8, ["src"])) : createCommentVNode("", !0)]))), 128))]);
13795
13798
  }
13796
- var j$3, M$3, __name, N$3, te$2, P$2, ne$3, __commonJSMin, __exportAll, __copyProps, __toESM, F$3, re$3, ie$3, I$2, ae$3, setPinia, getPinia, oe$3, useCardPreviewModeStore, se$3, ce$3, le$3, ue$2, de$2, fe$2, pe$3, me$2, L$2, R$2, z$2, he$2, ge$2, _e$1, B$2, ve$2, V$2, ye$2, be$1, xe$1, H$2, Se$1, U$2, Ce$1, we$1, W$3, Te$2, Ee$2, De$2, Oe$2, ke$3, Ae$2, je$2, Me$2, Ne$2, Pe$2, Fe$2, G$3, Ie$2, Le$2, Re$2, ze$2, Be$2, Ve$2, He$2, Ue$2, We$2, Ge$2, Ke$2, qe$2, Je$2, Ye$2, Xe$2, Ze$2, Qe$2, $e$2, K$3, et$2, tt$2, nt$2, rt$2, it$2, at$2, ot$2, q$3, J$3, st$2, getEscapeReplacement, _Tokenizer, Y$3, _Renderer, _TextRenderer, X$3, _Hooks, Marked, Z$4, ct$2, lt$2, ut$2, Q$3, dt$2, ft$2, pt$2, mt$2, ht$2, gt$2, _t$1, vt$2, yt$1, bt$1, $$4, xt$1, St$1, Ct$1, wt$1, Tt$1, Et$1, Dt$1, MODES, Ot$1, kt$1, At$1, jt$1, Mt$1, Nt$1, Pt$1, Ft$1, It$1, Lt$1, Rt$1, zt$1, Bt$1, Vt$1, Ht$1, Ut$1, Wt$1, Gt$1, Kt$1, qt$1, Jt$1, Yt$1, Xt$1, Zt$1, Qt$1, $t$1, en$1, tn$1, nn$1, rn$1, an$1, on$2, sn$1, cn$1, ln$1, un$1, dn$1, fn$1, pn$1, mn$1, hn$1, gn$1, _n$1, vn$1, yn$1, init_MarkdownRenderer_DoVbFpA6 = __esmMin((() => {
13799
+ var j$3, M$3, __name, N$3, te$2, P$2, ne$3, __commonJSMin, __exportAll, __copyProps, __toESM, F$3, re$3, ie$3, I$2, ae$3, setPinia, getPinia, oe$3, useCardPreviewModeStore, se$3, ce$3, le$3, ue$2, de$2, fe$2, pe$3, me$2, L$2, R$2, z$2, he$2, ge$2, _e$1, B$2, ve$2, V$2, ye$2, be$1, xe$1, H$2, Se$1, U$2, Ce$1, we$1, W$3, Te$2, Ee$2, De$2, Oe$2, ke$3, Ae$2, je$2, Me$2, Ne$2, Pe$2, Fe$2, G$3, Ie$2, Le$2, Re$2, ze$2, Be$2, Ve$2, He$2, Ue$2, We$2, Ge$2, Ke$2, qe$2, Je$2, Ye$2, Xe$2, Ze$2, Qe$2, $e$2, K$3, et$2, tt$2, nt$2, rt$2, it$2, at$2, ot$2, q$3, J$3, st$2, getEscapeReplacement, _Tokenizer, Y$3, _Renderer, _TextRenderer, X$3, _Hooks, Marked, Z$4, ct$2, lt$2, ut$2, Q$3, dt$2, ft$2, pt$2, mt$2, ht$2, gt$2, _t$1, vt$2, yt$1, bt$1, $$4, xt$1, St$1, Ct$1, wt$1, Tt$1, Et$1, Dt$1, MODES, Ot$1, kt$1, At$1, jt$1, Mt$1, Nt$1, Pt$1, Ft$1, It$1, Lt$1, Rt$1, zt$1, Bt$1, Vt$1, Ht$1, Ut$1, Wt$1, Gt$1, Kt$1, qt$1, Jt$1, Yt$1, Xt$1, Zt$1, Qt$1, $t$1, en$1, tn$1, nn$1, rn$1, an$1, on$2, sn$1, cn$1, ln$1, un$1, dn$1, fn$1, pn$1, mn$1, hn$1, gn$1, _n$1, vn$1, yn$1, init_MarkdownRenderer_kStoDRNE = __esmMin((() => {
13797
13800
  init_vue_runtime_esm_bundler(), init_moment(), init_dist$1(), init_pinia(), j$3 = Object.create, M$3 = Object.defineProperty, __name = (t, c) => M$3(t, "name", {
13798
13801
  value: c,
13799
13802
  configurable: !0
@@ -24629,6 +24632,8 @@ var init_v4 = __esmMin((() => {
24629
24632
  ContentNavigator: () => ContentNavigator,
24630
24633
  CouchDBToStaticPacker: () => CouchDBToStaticPacker,
24631
24634
  CourseLookup: () => CourseLookup,
24635
+ DIVERSITY_FLOOR: () => DIVERSITY_FLOOR,
24636
+ DIVERSITY_STRENGTH: () => DIVERSITY_STRENGTH,
24632
24637
  DocType: () => DocType,
24633
24638
  DocTypePrefixes: () => DocTypePrefixes,
24634
24639
  ENV: () => ENV,
@@ -24654,6 +24659,7 @@ var init_v4 = __esmMin((() => {
24654
24659
  computeSpread: () => computeSpread,
24655
24660
  computeStrategyGradient: () => computeStrategyGradient,
24656
24661
  createOrchestrationContext: () => createOrchestrationContext,
24662
+ diversityRerank: () => diversityRerank,
24657
24663
  docIsDeleted: () => docIsDeleted,
24658
24664
  endSessionTracking: () => endSessionTracking,
24659
24665
  ensureAppDataDirectory: () => ensureAppDataDirectory,
@@ -24908,6 +24914,47 @@ function getCourseDB(t) {
24908
24914
  let c = `coursedb-${t}`;
24909
24915
  return new pouchdb_setup_default(ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + c, createPouchDBConfig());
24910
24916
  }
24917
+ function diversityRerank(t, c = {}) {
24918
+ let u = c.strength ?? DIVERSITY_STRENGTH, d = c.floor ?? DIVERSITY_FLOOR, m = t.length;
24919
+ if (m <= 1) return t;
24920
+ let g = /* @__PURE__ */ new Map();
24921
+ for (let c of t) for (let t of c.tags ?? []) g.set(t, (g.get(t) ?? 0) + 1);
24922
+ let b = /* @__PURE__ */ new Map();
24923
+ for (let [t, c] of g) b.set(t, Math.log(m / c));
24924
+ let S = [...t], C = /* @__PURE__ */ new Map(), w = [], repetitionLoad = (t) => {
24925
+ let c = 0;
24926
+ for (let u of t.tags ?? []) {
24927
+ let t = C.get(u);
24928
+ t && (c += (b.get(u) ?? 0) * t);
24929
+ }
24930
+ return c;
24931
+ };
24932
+ for (; S.length > 0;) {
24933
+ let t = 0, c = -Infinity, m = 1, g = 0;
24934
+ for (let b = 0; b < S.length; b++) {
24935
+ let C = S[b], w = repetitionLoad(C), T = w > 0 ? Math.max(d, 1 / (1 + u * w)) : 1, E = C.score * T;
24936
+ E > c && (c = E, t = b, m = T, g = w);
24937
+ }
24938
+ let [b] = S.splice(t, 1);
24939
+ if (Number.isFinite(b.score) && m < 1) {
24940
+ let t = b.score * m;
24941
+ w.push({
24942
+ ...b,
24943
+ score: t,
24944
+ provenance: [...b.provenance, {
24945
+ strategy: STRATEGY,
24946
+ strategyId: STRATEGY_ID,
24947
+ strategyName: STRATEGY_NAME,
24948
+ action: "penalized",
24949
+ score: t,
24950
+ reason: `repeated tags (load ${g.toFixed(2)}) \u2192 \xD7${m.toFixed(2)}`
24951
+ }]
24952
+ });
24953
+ } else w.push(b);
24954
+ for (let t of b.tags ?? []) C.set(t, (C.get(t) ?? 0) + 1);
24955
+ }
24956
+ return w;
24957
+ }
24911
24958
  function registerPipelineForDebug(t) {
24912
24959
  _activePipeline = t;
24913
24960
  }
@@ -25507,7 +25554,7 @@ function logResultCards(t) {
25507
25554
  if (!(!VERBOSE_RESULTS || t.length === 0)) {
25508
25555
  logger.info(`[Pipeline] Results (${t.length} cards):`);
25509
25556
  for (let c = 0; c < t.length; c++) {
25510
- let u = t[c], d = u.tags?.slice(0, 3).join(", ") || "", m = u.provenance.filter((t) => t.strategy === "hierarchyDefinition" || t.strategy === "priorityDefinition" || t.strategy === "interferenceFilter" || t.strategy === "letterGating" || t.strategy === "ephemeralHint").map((t) => {
25557
+ let u = t[c], d = u.tags?.slice(0, 3).join(", ") || "", m = u.provenance.filter((t) => t.strategy === "hierarchyDefinition" || t.strategy === "priorityDefinition" || t.strategy === "interferenceFilter" || t.strategy === "letterGating" || t.strategy === "ephemeralHint" || t.strategy === "diversityRerank").map((t) => {
25511
25558
  let c = t.action === "boosted" ? "↑" : t.action === "penalized" ? "↓" : "=";
25512
25559
  return `${t.strategyName}${c}${t.score.toFixed(2)}`;
25513
25560
  }).join(" | ");
@@ -26503,7 +26550,7 @@ function toggleSessionOverlay() {
26503
26550
  overlayEl ? (teardown(), logger.info("[Session Overlay] Hidden.")) : (mount(), logger.info("[Session Overlay] Shown. Toggle off with window.skuilder.session.dbgOverlay()."));
26504
26551
  }
26505
26552
  function mount() {
26506
- overlayEl = document.createElement("div"), overlayEl.id = OVERLAY_ID, Object.assign(overlayEl.style, {
26553
+ minified = !1, overlayEl = document.createElement("div"), overlayEl.id = OVERLAY_ID, Object.assign(overlayEl.style, {
26507
26554
  position: "fixed",
26508
26555
  top: "8px",
26509
26556
  left: "8px",
@@ -26529,19 +26576,45 @@ function render() {
26529
26576
  spinnerFrame++;
26530
26577
  let t = getActiveController();
26531
26578
  if (!t) {
26532
- overlayEl.innerHTML = headerHtml() + "<div style=\"opacity:.65\">No active session.</div>";
26579
+ lastSnapshot = null, overlayEl.innerHTML = headerHtml() + (minified ? "" : "<div style=\"opacity:.65\">No active session.</div>"), attachHandlers();
26533
26580
  return;
26534
26581
  }
26535
26582
  let c = t.getDebugSnapshot();
26536
- overlayEl.innerHTML = headerHtml() + replanHtml(c) + metaHtml(c) + hintsHtml(c.sessionHints) + queueHtml("reviewQ", "reviewQ", c.reviewQ) + queueHtml("newQ", "newQ", c.newQ) + queueHtml("failedQ", "failedQ", c.failedQ), overlayEl.querySelectorAll("[data-q]").forEach((t) => {
26583
+ if (lastSnapshot = c, minified) {
26584
+ overlayEl.innerHTML = headerHtml(), attachHandlers();
26585
+ return;
26586
+ }
26587
+ overlayEl.innerHTML = headerHtml() + replanHtml(c) + metaHtml(c) + hintsHtml(c.sessionHints) + queueHtml("reviewQ", "reviewQ", c.reviewQ) + queueHtml("newQ", "newQ", c.newQ) + queueHtml("failedQ", "failedQ", c.failedQ) + drawnHtml("drawn", c.drawnCards), attachHandlers();
26588
+ }
26589
+ function attachHandlers() {
26590
+ if (!overlayEl) return;
26591
+ overlayEl.querySelectorAll("[data-q]").forEach((t) => {
26537
26592
  t.onclick = () => {
26538
26593
  let c = t.dataset.q;
26539
26594
  c && (expanded[c] = !expanded[c], render());
26540
26595
  };
26541
26596
  });
26597
+ let t = overlayEl.querySelector("[data-copy]");
26598
+ t && (t.onclick = (t) => {
26599
+ t.stopPropagation(), copySnapshot();
26600
+ });
26601
+ let c = overlayEl.querySelector("[data-min]");
26602
+ c && (c.onclick = (t) => {
26603
+ t.stopPropagation(), minified = !minified, render();
26604
+ });
26605
+ }
26606
+ function copySnapshot() {
26607
+ let t = snapshotToText(lastSnapshot), flash = () => {
26608
+ copyFlashUntil = Date.now() + 1200, render();
26609
+ }, c = typeof navigator < "u" ? navigator.clipboard : void 0;
26610
+ c?.writeText ? c.writeText(t).then(flash, (t) => {
26611
+ logger.warn(`[Session Overlay] Clipboard write failed: ${String(t)}`);
26612
+ }) : logger.info(`[Session Overlay] Clipboard unavailable; snapshot follows:
26613
+ ${t}`);
26542
26614
  }
26543
26615
  function headerHtml() {
26544
- return "<div style=\"font-weight:600;color:#93c5fd;margin-bottom:4px\">⚙ SessionController</div>";
26616
+ let t = Date.now() < copyFlashUntil;
26617
+ return `<div style="font-weight:600;color:#93c5fd;margin-bottom:${minified ? 0 : 4}px">${`<span data-copy style="cursor:pointer;float:right;font-weight:400;color:${t ? "#86efac" : "#93c5fd"};border:1px solid currentColor;border-radius:4px;padding:0 4px;line-height:1.3">${t ? "✓ copied" : "⎘ copy"}</span>`}${`<span data-min title="${minified ? "Restore" : "Minify"}" style="cursor:pointer;float:right;font-weight:400;color:#93c5fd;border:1px solid currentColor;border-radius:4px;padding:0 5px;margin-right:4px;line-height:1.3">${minified ? "▢" : "—"}</span>`}\u2699 SessionController</div>`;
26545
26618
  }
26546
26619
  function replanHtml(t) {
26547
26620
  return t.replanActive ? `<div style="margin-bottom:6px;color:#fde047">${SPINNER_FRAMES[spinnerFrame % SPINNER_FRAMES.length]} replanning <span style="opacity:.85">[${esc(t.replanLabel ?? "(auto)")}]</span></div>` : "<div style=\"margin-bottom:6px;opacity:.45\">○ idle</div>";
@@ -26558,8 +26631,44 @@ function hintsHtml(t) {
26558
26631
  return t && (t.boostTags && Object.keys(t.boostTags).length && c.push("boost: " + Object.entries(t.boostTags).map(([t, c]) => `${esc(t)}<span style="opacity:.6">\xD7${c}</span>`).join(", ")), t.boostCards && Object.keys(t.boostCards).length && c.push("boostCards: " + Object.entries(t.boostCards).map(([t, c]) => `${esc(t)}<span style="opacity:.6">\xD7${c}</span>`).join(", ")), t.requireCards?.length && c.push(`require: ${t.requireCards.map(esc).join(", ")}`), t.requireTags?.length && c.push(`requireTags: ${t.requireTags.map(esc).join(", ")}`), t.excludeTags?.length && c.push(`exclude: ${t.excludeTags.map(esc).join(", ")}`), t.excludeCards?.length && c.push(`excludeCards: ${t.excludeCards.map(esc).join(", ")}`)), `<div style="margin-bottom:6px"><div style="color:#86efac">sessionHints</div>${c.length ? c.map((t) => `<div style="margin-left:6px">${t}</div>`).join("") : "<div style=\"margin-left:6px;opacity:.6\">none</div>"}</div>`;
26559
26632
  }
26560
26633
  function queueHtml(t, c, u) {
26561
- let d = u.length > INLINE_THRESHOLD, m = !d || expanded[t], g = d ? expanded[t] ? "▾ " : "▸ " : "", b = u.dequeueCount ? ` <span style="opacity:.5">drawn ${u.dequeueCount}</span>` : "", S = d ? "cursor:pointer;color:#f9a8d4" : "color:#f9a8d4", C = `<div${d ? ` data-q="${t}"` : ""} style="${S}">${g}${c}: ${u.length}${b}</div>`, w = "";
26562
- return w = m && u.cards.length ? "<ol style=\"margin:2px 0 6px 0;padding-left:20px\">" + u.cards.map((t) => `<li style="white-space:nowrap">${esc(t)}</li>`).join("") + "</ol>" : u.cards.length ? `<div style="margin:1px 0 6px 6px;opacity:.55">(${u.length} cards \u2014 click to expand)</div>` : "<div style=\"margin:1px 0 6px 6px;opacity:.5\">empty</div>", C + w;
26634
+ let d = u.length > INLINE_THRESHOLD, m = d && expanded[t], g = d ? expanded[t] ? "▾ " : "▸ " : "", b = u.dequeueCount ? ` <span style="opacity:.5">drawn ${u.dequeueCount}</span>` : "", S = d ? "cursor:pointer;color:#f9a8d4" : "color:#f9a8d4", C = `<div${d ? ` data-q="${t}"` : ""} style="${S}">${g}${c}: ${u.length}${b}</div>`;
26635
+ if (!u.cards.length) return C + "<div style=\"margin:1px 0 6px 6px;opacity:.5\">empty</div>";
26636
+ let w = m ? u.cards : u.cards.slice(0, INLINE_THRESHOLD), T = u.length - w.length, E = `<ol style="margin:2px 0 ${d ? 2 : 6}px 0;padding-left:20px">` + w.map((t) => `<li style="white-space:nowrap">${esc(t)}</li>`).join("") + "</ol>";
26637
+ if (d) {
26638
+ let c = m ? "▾ show less" : `\u2026 +${T} more`;
26639
+ E += `<div data-q="${t}" style="cursor:pointer;margin:0 0 6px 20px;opacity:.6">${c}</div>`;
26640
+ }
26641
+ return C + E;
26642
+ }
26643
+ function outcomeGlyph(t) {
26644
+ return t === !0 ? "<span style=\"color:#86efac\">✓</span>" : t === !1 ? "<span style=\"color:#fca5a5\">✗</span>" : "<span style=\"opacity:.5\">·</span>";
26645
+ }
26646
+ function drawnHtml(t, c) {
26647
+ let u = c.length > INLINE_THRESHOLD, d = u && expanded[t], m = u ? expanded[t] ? "▾ " : "▸ " : "", g = u ? "cursor:pointer;color:#c4b5fd" : "color:#c4b5fd", b = `<div${u ? ` data-q="${t}"` : ""} style="${g}">${m}drawn: ${c.length}</div>`;
26648
+ if (!c.length) return b + "<div style=\"margin:1px 0 6px 6px;opacity:.5\">none yet</div>";
26649
+ let S = d ? c : c.slice(0, INLINE_THRESHOLD), C = c.length - S.length, w = `<ol style="margin:2px 0 ${u ? 2 : 6}px 0;padding-left:20px">${S.map((t) => {
26650
+ let c = t.attempts > 1 ? `<span style="opacity:.5"> \xD7${t.attempts}</span>` : "", u = `<span style="opacity:.45"> ${Math.round(t.timeSpentMs / 100) / 10}s</span>`;
26651
+ return `<li style="white-space:nowrap">${outcomeGlyph(t.correct)} ${esc(t.cardID)}<span style="opacity:.5"> [${esc(t.status)}]</span>${c}${u}</li>`;
26652
+ }).join("")}</ol>`;
26653
+ if (u) {
26654
+ let c = d ? "▾ show less" : `\u2026 +${C} more`;
26655
+ w += `<div data-q="${t}" style="cursor:pointer;margin:0 0 6px 20px;opacity:.6">${c}</div>`;
26656
+ }
26657
+ return b + w;
26658
+ }
26659
+ function snapshotToText(t) {
26660
+ if (!t) return "SessionController — no active session.";
26661
+ let c = [];
26662
+ c.push("=== SessionController ==="), c.push(`time ${formatTime(t.secondsRemaining)}`), t.hasCardGuarantee && c.push(`guarantee: ${t.minCardsGuarantee}`), c.push(`well-indicated left: ${t.wellIndicatedRemaining}`), c.push(`current: ${t.currentCard ?? "—"}`), c.push(t.replanActive ? `replan: ACTIVE [${t.replanLabel ?? "(auto)"}]` : "replan: idle"), c.push(""), c.push("sessionHints:");
26663
+ let u = t.sessionHints, d = [];
26664
+ u && (u.boostTags && Object.keys(u.boostTags).length && d.push(` boost: ${Object.entries(u.boostTags).map(([t, c]) => `${t}\xD7${c}`).join(", ")}`), u.boostCards && Object.keys(u.boostCards).length && d.push(` boostCards: ${Object.entries(u.boostCards).map(([t, c]) => `${t}\xD7${c}`).join(", ")}`), u.requireCards?.length && d.push(` require: ${u.requireCards.join(", ")}`), u.requireTags?.length && d.push(` requireTags: ${u.requireTags.join(", ")}`), u.excludeTags?.length && d.push(` exclude: ${u.excludeTags.join(", ")}`), u.excludeCards?.length && d.push(` excludeCards: ${u.excludeCards.join(", ")}`)), c.push(d.length ? d.join("\n") : " none");
26665
+ let queueText = (t, u) => {
26666
+ c.push(""), c.push(`${t}: ${u.length} (drawn ${u.dequeueCount})`), u.cards.forEach((t, u) => c.push(` ${u + 1}. ${t}`));
26667
+ };
26668
+ return queueText("reviewQ", t.reviewQ), queueText("newQ", t.newQ), queueText("failedQ", t.failedQ), c.push(""), c.push(`drawn: ${t.drawnCards.length}`), t.drawnCards.forEach((t, u) => {
26669
+ let d = t.correct === !0 ? "✓" : t.correct === !1 ? "✗" : "·", m = `${Math.round(t.timeSpentMs / 100) / 10}s`;
26670
+ c.push(` ${u + 1}. ${d} ${t.cardID} [${t.status}] \xD7${t.attempts} ${m}`);
26671
+ }), c.join("\n");
26563
26672
  }
26564
26673
  function formatTime(t) {
26565
26674
  let c = Math.max(0, Math.round(t));
@@ -26678,7 +26787,7 @@ function mountSessionDebugger() {
26678
26787
  let t = window;
26679
26788
  t.skuilder = t.skuilder || {}, t.skuilder.session = sessionDebugAPI;
26680
26789
  }
26681
- var import___vite_browser_external, import___vite_browser_external$1, import___vite_browser_external$2, import_browser_ponyfill, import_browser_ponyfill$1, import_browser, _SessionController2, __defProp, __getOwnPropNames, __glob, __esm, __export, init_adminDB, init_classroomDB, init_SyncStrategy, isDevelopment, logger, init_logger, GuestUsername, log, DocType, DocTypePrefixes, init_types_legacy, init_util, pouchdb_setup_default, init_pouchdb_setup, init_dataDirectory, REVIEW_TIME_FORMAT, log2, init_userDBHelpers, Loggable, init_Loggable, UpdateQueue, init_updateQueue, UsrCrsData, init_user_course_relDB, CLIENT_CACHE, init_clientCache, AlreadyTaggedErr, init_courseAPI, courseLookupDBTitle, CourseLookup, init_courseLookupDB, PipelineDebugger_exports, _activePipeline, MAX_RUNS, runHistory, DISCARDED_KEEP_TOP, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI, init_PipelineDebugger, CompositeGenerator_exports, AggregationMode, DEFAULT_AGGREGATION_MODE, FREQUENCY_BOOST_FACTOR, CompositeGenerator, init_CompositeGenerator, elo_exports, ELONavigator, init_elo, generators_exports, init_generators, prescribed_exports, DEFAULT_FRESHNESS_WINDOW, DEFAULT_MAX_DIRECT_PER_RUN, DEFAULT_MAX_SUPPORT_PER_RUN, DEFAULT_HIERARCHY_DEPTH, DEFAULT_MIN_COUNT, BASE_TARGET_SCORE, BASE_SUPPORT_SCORE, DISCOVERED_SUPPORT_SCORE, MAX_TARGET_MULTIPLIER, MAX_SUPPORT_MULTIPLIER, PRESCRIBED_DEBUG_VERSION, PrescribedCardsGenerator, init_prescribed, srs_exports, DEFAULT_HEALTHY_BACKLOG, MAX_BACKLOG_PRESSURE, SRSNavigator, init_srs, types_exports, init_types, globImport_generators, init_, DEFAULT_LEARNABLE_WEIGHT, init_contentNavigationStrategy, WeightedFilter_exports, WeightedFilter, init_WeightedFilter, eloDistance_exports, DEFAULT_HALF_LIFE, DEFAULT_MIN_MULTIPLIER, DEFAULT_MAX_MULTIPLIER, init_eloDistance, hierarchyDefinition_exports, DEFAULT_MIN_COUNT2, HierarchyDefinitionNavigator, init_hierarchyDefinition, userTagPreference_exports, UserTagPreferenceFilter, init_userTagPreference, filters_exports, init_filters, inferredPreferenceStub_exports, INFERRED_PREFERENCE_NAVIGATOR_STUB, init_inferredPreferenceStub, interferenceMitigator_exports, DEFAULT_MIN_COUNT3, DEFAULT_MIN_ELAPSED_DAYS, DEFAULT_INTERFERENCE_DECAY, InterferenceMitigatorNavigator, init_interferenceMitigator, relativePriority_exports, DEFAULT_PRIORITY, DEFAULT_PRIORITY_INFLUENCE, DEFAULT_COMBINE_MODE, RelativePriorityNavigator, init_relativePriority, types_exports2, init_types2, userGoalStub_exports, USER_GOAL_NAVIGATOR_STUB, init_userGoalStub, globImport_filters, init_2, init_gradient, MIN_OBSERVATIONS_FOR_UPDATE, LEARNING_RATE, MAX_WEIGHT_DELTA, MIN_R_SQUARED_FOR_GRADIENT, FLAT_GRADIENT_THRESHOLD, MAX_HISTORY_LENGTH, init_learning, init_signal, init_recording, MIN_SPREAD, MAX_SPREAD, MIN_WEIGHT, MAX_WEIGHT, init_orchestration, Pipeline_exports, VERBOSE_RESULTS, Pipeline, init_Pipeline, defaults_exports, init_defaults, PipelineAssembler_exports, PipelineAssembler, init_PipelineAssembler, globImport, init_3, navigators_exports, navigatorRegistry, Navigators, NavigatorRole, NavigatorRoles, ContentNavigator, init_navigators, CoursesDB, CourseDB, init_courseDB, classroomLookupDBTitle, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB, TeacherClassroomDB, ClassroomLookupDB, init_classroomDB2, AdminDB, init_adminDB2, CourseSyncService_exports, DEFAULT_REPLICATION, CourseSyncService, init_CourseSyncService, init_auth, CouchDBSyncStrategy_exports, log3, CouchDBSyncStrategy, init_CouchDBSyncStrategy, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_TIME_FORMAT2, init_couch, log4, BaseUser, userCoursesDoc, userClassroomsDoc, init_BaseUserDB, init_common, PouchDataLayerProvider_exports, CouchDataLayerProvider, init_PouchDataLayerProvider, pathUtils, nodeFS, StaticDataUnpacker, init_StaticDataUnpacker, StaticCourseDB, init_courseDB2, StaticCoursesDB, init_coursesDB, NoOpSyncStrategy, init_NoOpSyncStrategy, StaticDataLayerProvider_exports, StaticDataLayerProvider, init_StaticDataLayerProvider, NOT_SET, ENV, dataLayerInstance, init_factory, TagFilteredContentSource, init_TagFilteredContentSource, init_contentSource, init_courseDB3, init_dataLayerProvider, init_userDB, init_interfaces, init_user, init_strategyState, init_userOutcome, init_cardProcessor, init_types3, init_bulkImport, userDBDebugAPI, init_UserDBDebugger, init_core, duration, SrsService, EloService, ResponseProcessor, CardHydrationService, ItemQueue, CouchDBToStaticPacker, DEFAULT_MIGRATION_OPTIONS, FileSystemError, nodeFS2, nodeFS3, nodePath, StaticToCouchDBMigrator, QuotaRoundRobinMixer, MAX_RUNS2, runHistory2, mixerDebugAPI, activeController, OVERLAY_ID, POLL_MS, INLINE_THRESHOLD, SPINNER_FRAMES, spinnerFrame, overlayEl, pollHandle, expanded, activeSession, sessionHistory, MAX_HISTORY, sessionDebugAPI, SessionController, init_dist = __esmMin((() => {
26790
+ var import___vite_browser_external, import___vite_browser_external$1, import___vite_browser_external$2, import_browser_ponyfill, import_browser_ponyfill$1, import_browser, _SessionController2, __defProp, __getOwnPropNames, __glob, __esm, __export, init_adminDB, init_classroomDB, init_SyncStrategy, isDevelopment, logger, init_logger, GuestUsername, log, DocType, DocTypePrefixes, init_types_legacy, init_util, pouchdb_setup_default, init_pouchdb_setup, init_dataDirectory, REVIEW_TIME_FORMAT, log2, init_userDBHelpers, Loggable, init_Loggable, UpdateQueue, init_updateQueue, UsrCrsData, init_user_course_relDB, CLIENT_CACHE, init_clientCache, AlreadyTaggedErr, init_courseAPI, courseLookupDBTitle, CourseLookup, init_courseLookupDB, diversityRerank_exports, DIVERSITY_STRENGTH, DIVERSITY_FLOOR, STRATEGY, STRATEGY_ID, STRATEGY_NAME, init_diversityRerank, PipelineDebugger_exports, _activePipeline, MAX_RUNS, runHistory, DISCARDED_KEEP_TOP, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI, init_PipelineDebugger, CompositeGenerator_exports, AggregationMode, DEFAULT_AGGREGATION_MODE, FREQUENCY_BOOST_FACTOR, CompositeGenerator, init_CompositeGenerator, elo_exports, ELO_RELEVANCE_SIGMA, ELONavigator, init_elo, generators_exports, init_generators, prescribed_exports, DEFAULT_FRESHNESS_WINDOW, DEFAULT_MAX_DIRECT_PER_RUN, DEFAULT_MAX_SUPPORT_PER_RUN, DEFAULT_HIERARCHY_DEPTH, DEFAULT_MIN_COUNT, DEFAULT_PRACTICE_MIN_COUNT, DEFAULT_MAX_PRACTICE_PER_RUN, BASE_TARGET_SCORE, BASE_SUPPORT_SCORE, DISCOVERED_SUPPORT_SCORE, BASE_PRACTICE_SCORE, MAX_TARGET_MULTIPLIER, MAX_SUPPORT_MULTIPLIER, PRESCRIBED_DEBUG_VERSION, PrescribedCardsGenerator, init_prescribed, srs_exports, DEFAULT_HEALTHY_BACKLOG, MAX_BACKLOG_PRESSURE, SRSNavigator, init_srs, types_exports, init_types, globImport_generators, init_, DEFAULT_LEARNABLE_WEIGHT, init_contentNavigationStrategy, WeightedFilter_exports, WeightedFilter, init_WeightedFilter, eloDistance_exports, DEFAULT_HALF_LIFE, DEFAULT_MIN_MULTIPLIER, DEFAULT_MAX_MULTIPLIER, init_eloDistance, hierarchyDefinition_exports, DEFAULT_MIN_COUNT2, HierarchyDefinitionNavigator, init_hierarchyDefinition, userTagPreference_exports, UserTagPreferenceFilter, init_userTagPreference, filters_exports, init_filters, inferredPreferenceStub_exports, INFERRED_PREFERENCE_NAVIGATOR_STUB, init_inferredPreferenceStub, interferenceMitigator_exports, DEFAULT_MIN_COUNT3, DEFAULT_MIN_ELAPSED_DAYS, DEFAULT_INTERFERENCE_DECAY, InterferenceMitigatorNavigator, init_interferenceMitigator, relativePriority_exports, DEFAULT_PRIORITY, DEFAULT_PRIORITY_INFLUENCE, DEFAULT_COMBINE_MODE, RelativePriorityNavigator, init_relativePriority, types_exports2, init_types2, userGoalStub_exports, USER_GOAL_NAVIGATOR_STUB, init_userGoalStub, globImport_filters, init_2, init_gradient, MIN_OBSERVATIONS_FOR_UPDATE, LEARNING_RATE, MAX_WEIGHT_DELTA, MIN_R_SQUARED_FOR_GRADIENT, FLAT_GRADIENT_THRESHOLD, MAX_HISTORY_LENGTH, init_learning, init_signal, init_recording, MIN_SPREAD, MAX_SPREAD, MIN_WEIGHT, MAX_WEIGHT, init_orchestration, Pipeline_exports, VERBOSE_RESULTS, Pipeline, init_Pipeline, defaults_exports, init_defaults, PipelineAssembler_exports, PipelineAssembler, init_PipelineAssembler, globImport, init_3, navigators_exports, navigatorRegistry, Navigators, NavigatorRole, NavigatorRoles, ContentNavigator, init_navigators, CoursesDB, CourseDB, init_courseDB, classroomLookupDBTitle, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB, TeacherClassroomDB, ClassroomLookupDB, init_classroomDB2, AdminDB, init_adminDB2, CourseSyncService_exports, DEFAULT_REPLICATION, CourseSyncService, init_CourseSyncService, init_auth, CouchDBSyncStrategy_exports, log3, CouchDBSyncStrategy, init_CouchDBSyncStrategy, isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_TIME_FORMAT2, init_couch, log4, BaseUser, userCoursesDoc, userClassroomsDoc, init_BaseUserDB, init_common, PouchDataLayerProvider_exports, CouchDataLayerProvider, init_PouchDataLayerProvider, pathUtils, nodeFS, StaticDataUnpacker, init_StaticDataUnpacker, StaticCourseDB, init_courseDB2, StaticCoursesDB, init_coursesDB, NoOpSyncStrategy, init_NoOpSyncStrategy, StaticDataLayerProvider_exports, StaticDataLayerProvider, init_StaticDataLayerProvider, NOT_SET, ENV, dataLayerInstance, init_factory, TagFilteredContentSource, init_TagFilteredContentSource, init_contentSource, init_courseDB3, init_dataLayerProvider, init_userDB, init_interfaces, init_user, init_strategyState, init_userOutcome, init_cardProcessor, init_types3, init_bulkImport, userDBDebugAPI, init_UserDBDebugger, init_core, duration, SrsService, EloService, ResponseProcessor, CardHydrationService, ItemQueue, CouchDBToStaticPacker, DEFAULT_MIGRATION_OPTIONS, FileSystemError, nodeFS2, nodeFS3, nodePath, StaticToCouchDBMigrator, QuotaRoundRobinMixer, MAX_RUNS2, runHistory2, mixerDebugAPI, activeController, OVERLAY_ID, POLL_MS, INLINE_THRESHOLD, SPINNER_FRAMES, spinnerFrame, overlayEl, pollHandle, lastSnapshot, copyFlashUntil, minified, expanded, activeSession, sessionHistory, MAX_HISTORY, sessionDebugAPI, SessionController, init_dist = __esmMin((() => {
26682
26791
  init_defineProperty(), init_index_browser_es$5(), init_index_browser_es(), init_index_es(), import___vite_browser_external = /* @__PURE__ */ __toESM$2(require___vite_browser_external(), 1), import___vite_browser_external$1 = /* @__PURE__ */ __toESM$2(require___vite_browser_external(), 1), import___vite_browser_external$2 = /* @__PURE__ */ __toESM$2(require___vite_browser_external(), 1), init_moment(), init_dist$1(), init_esm_browser(), import_browser_ponyfill = /* @__PURE__ */ __toESM$2(require_browser_ponyfill(), 1), import_browser_ponyfill$1 = /* @__PURE__ */ __toESM$2(require_browser_ponyfill(), 1), import_browser = /* @__PURE__ */ __toESM$2(require_browser(), 1), __defProp = Object.defineProperty, __getOwnPropNames = Object.getOwnPropertyNames, __glob = (t) => (c) => {
26683
26792
  var u = t[c];
26684
26793
  if (u) return u();
@@ -26878,6 +26987,12 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
26878
26987
  }
26879
26988
  }
26880
26989
  }, _defineProperty$2(t, "_dbInstance", null), t);
26990
+ } }), diversityRerank_exports = {}, __export(diversityRerank_exports, {
26991
+ DIVERSITY_FLOOR: () => DIVERSITY_FLOOR,
26992
+ DIVERSITY_STRENGTH: () => DIVERSITY_STRENGTH,
26993
+ diversityRerank: () => diversityRerank
26994
+ }), init_diversityRerank = __esm({ "src/core/navigators/diversityRerank.ts"() {
26995
+ DIVERSITY_STRENGTH = .6, DIVERSITY_FLOOR = .3, STRATEGY = "diversityRerank", STRATEGY_ID = "DIVERSITY_RERANK", STRATEGY_NAME = "Diversity Re-rank";
26881
26996
  } }), PipelineDebugger_exports = {}, __export(PipelineDebugger_exports, {
26882
26997
  buildRunReport: () => buildRunReport,
26883
26998
  captureRun: () => captureRun,
@@ -27194,7 +27309,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27194
27309
  }
27195
27310
  };
27196
27311
  } }), elo_exports = {}, __export(elo_exports, { default: () => ELONavigator }), init_elo = __esm({ "src/core/navigators/generators/elo.ts"() {
27197
- init_navigators(), init_logger(), ELONavigator = class extends ContentNavigator {
27312
+ init_navigators(), init_logger(), ELO_RELEVANCE_SIGMA = 300, ELONavigator = class extends ContentNavigator {
27198
27313
  constructor(t, c, u) {
27199
27314
  super(t, c, u), _defineProperty$2(this, "name", void 0), this.name = u?.name || "ELO";
27200
27315
  }
@@ -27208,7 +27323,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27208
27323
  ...t,
27209
27324
  status: "new"
27210
27325
  })).map((t) => {
27211
- let c = t.elo ?? 1e3, d = Math.abs(c - u), m = Math.max(0, 1 - d / 500), g = m > 0 ? Math.random() ** (1 / m) : 0;
27326
+ let c = t.elo ?? 1e3, d = Math.abs(c - u), m = Math.exp(-((d / ELO_RELEVANCE_SIGMA) ** 2)), g = m * (.5 + .5 * Math.random());
27212
27327
  return {
27213
27328
  cardId: t.cardID,
27214
27329
  courseId: t.courseID,
@@ -27219,7 +27334,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27219
27334
  strategyId: this.strategyId || "NAVIGATION_STRATEGY-ELO-default",
27220
27335
  action: "generated",
27221
27336
  score: g,
27222
- reason: `ELO distance ${Math.round(d)} (card: ${Math.round(c)}, user: ${Math.round(u)}), raw ${m.toFixed(3)}, key ${g.toFixed(3)}`
27337
+ reason: `ELO distance ${Math.round(d)} (card: ${Math.round(c)}, user: ${Math.round(u)}), relevance ${m.toFixed(3)}, key ${g.toFixed(3)}`
27223
27338
  }]
27224
27339
  };
27225
27340
  });
@@ -27233,7 +27348,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27233
27348
  }
27234
27349
  };
27235
27350
  } }), generators_exports = {}, init_generators = __esm({ "src/core/navigators/generators/index.ts"() {} }), prescribed_exports = {}, __export(prescribed_exports, { default: () => PrescribedCardsGenerator }), init_prescribed = __esm({ "src/core/navigators/generators/prescribed.ts"() {
27236
- init_navigators(), init_logger(), DEFAULT_FRESHNESS_WINDOW = 3, DEFAULT_MAX_DIRECT_PER_RUN = 3, DEFAULT_MAX_SUPPORT_PER_RUN = 3, DEFAULT_HIERARCHY_DEPTH = 2, DEFAULT_MIN_COUNT = 3, BASE_TARGET_SCORE = 1, BASE_SUPPORT_SCORE = .8, DISCOVERED_SUPPORT_SCORE = 12, MAX_TARGET_MULTIPLIER = 8, MAX_SUPPORT_MULTIPLIER = 4, PRESCRIBED_DEBUG_VERSION = "testversion-prescribed-v3", PrescribedCardsGenerator = class extends ContentNavigator {
27351
+ init_navigators(), init_logger(), DEFAULT_FRESHNESS_WINDOW = 3, DEFAULT_MAX_DIRECT_PER_RUN = 3, DEFAULT_MAX_SUPPORT_PER_RUN = 3, DEFAULT_HIERARCHY_DEPTH = 2, DEFAULT_MIN_COUNT = 3, DEFAULT_PRACTICE_MIN_COUNT = 3, DEFAULT_MAX_PRACTICE_PER_RUN = 4, BASE_TARGET_SCORE = 1, BASE_SUPPORT_SCORE = .8, DISCOVERED_SUPPORT_SCORE = 12, BASE_PRACTICE_SCORE = 1, MAX_TARGET_MULTIPLIER = 8, MAX_SUPPORT_MULTIPLIER = 4, PRESCRIBED_DEBUG_VERSION = "testversion-prescribed-v3", PrescribedCardsGenerator = class extends ContentNavigator {
27237
27352
  constructor(t, c, u) {
27238
27353
  super(t, c, u), _defineProperty$2(this, "name", void 0), _defineProperty$2(this, "config", void 0), this.name = u.name || "Prescribed Cards", this.config = this.parseConfig(u.serializedData), logger.debug(`[Prescribed] Initialized with ${this.config.groups.length} groups and ${this.config.groups.reduce((t, c) => t + c.targetCardIds.length, 0)} targets`);
27239
27354
  }
@@ -27271,8 +27386,18 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27271
27386
  userGlobalElo: T
27272
27387
  });
27273
27388
  Fr.push(c), logger.info(`[Prescribed] Group '${t.id}': ${t.targetCardIds.length} targets total, ${c.encounteredTargets.size} encountered, ${c.pendingTargets.length} pending (${c.surfaceableTargets.length} surfaceable, ${c.blockedTargets.length} blocked), ${c.supportCandidates.length} authored support candidates, ${c.discoveredSupportCandidates.length} discovered support candidates, pressure=${c.pressureMultiplier.toFixed(2)}`), c.blockedTargets.length > 0 && (logger.info(`[Prescribed] Group '${t.id}' blocked targets: ${c.blockedTargets.join(", ")}`), logger.info(`[Prescribed] Group '${t.id}' support tags needed: ${c.supportTags.join(", ") || "(none)"}`), logger.info(`[Prescribed] Group '${t.id}' escalation mode: ` + (c.supportCandidates.length > 0 ? "direct-support" : c.discoveredSupportCandidates.length > 0 ? "inserted-support-candidates" : "boost-only")), c.discoveredSupportCandidates.length > 0 && logger.info(`[Prescribed] Group '${t.id}' discovered support candidates: ${c.discoveredSupportCandidates.join(", ")}`)), Mr.groups[t.id] = this.buildNextGroupState(c, S.groups[t.id]);
27274
- let d = this.buildDirectTargetCards(c, u, Pr), g = this.buildSupportCards(c, u, Pr), w = this.buildDiscoveredSupportCards(c, u, Pr);
27275
- Nr.push(...d, ...g, ...w);
27389
+ let d = this.buildDirectTargetCards(c, u, Pr), g = this.buildSupportCards(c, u, Pr), w = this.buildDiscoveredSupportCards(c, u, Pr), D = this.buildPracticeCards({
27390
+ group: t,
27391
+ courseId: u,
27392
+ emittedIds: Pr,
27393
+ cardsByTag: jr,
27394
+ hierarchyConfigs: C,
27395
+ userTagElo: E,
27396
+ userGlobalElo: T,
27397
+ activeIds: m,
27398
+ seenIds: b
27399
+ });
27400
+ Nr.push(...d, ...g, ...w, ...D);
27276
27401
  }
27277
27402
  let Ir = this.buildSupportHintSummary(Fr), Lr = Object.keys(Ir.boostTags).length > 0 ? {
27278
27403
  boostTags: Ir.boostTags,
@@ -27290,7 +27415,9 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27290
27415
  } : { cards: [] };
27291
27416
  let Rr = pickTopByScore(Nr, t), zr = /* @__PURE__ */ new Map();
27292
27417
  for (let t of Rr) {
27293
- let c = t.provenance[0], u = c?.reason.match(/group=([^;]+)/)?.[1], d = c?.reason.includes("mode=support") ? "supportIds" : "targetIds";
27418
+ let c = t.provenance[0];
27419
+ if (c?.reason.includes("mode=practice")) continue;
27420
+ let u = c?.reason.match(/group=([^;]+)/)?.[1], d = c?.reason.includes("mode=support") ? "supportIds" : "targetIds";
27294
27421
  u && (zr.has(u) || zr.set(u, {
27295
27422
  targetIds: [],
27296
27423
  supportIds: []
@@ -27334,7 +27461,10 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27334
27461
  enabled: t.hierarchyWalk?.enabled !== !1,
27335
27462
  maxDepth: typeof t.hierarchyWalk?.maxDepth == "number" ? t.hierarchyWalk.maxDepth : DEFAULT_HIERARCHY_DEPTH
27336
27463
  },
27337
- retireOnEncounter: t.retireOnEncounter !== !1
27464
+ retireOnEncounter: t.retireOnEncounter !== !1,
27465
+ practiceTagPatterns: dedupe(Array.isArray(t.practiceTagPatterns) ? t.practiceTagPatterns.filter((t) => typeof t == "string") : []),
27466
+ practiceMinCount: typeof t.practiceMinCount == "number" ? t.practiceMinCount : DEFAULT_PRACTICE_MIN_COUNT,
27467
+ maxPracticeCardsPerRun: typeof t.maxPracticeCardsPerRun == "number" ? t.maxPracticeCardsPerRun : DEFAULT_MAX_PRACTICE_PER_RUN
27338
27468
  })).filter((t) => t.targetCardIds.length > 0) };
27339
27469
  } catch {
27340
27470
  return { groups: [] };
@@ -27462,6 +27592,41 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
27462
27592
  });
27463
27593
  return g;
27464
27594
  }
27595
+ buildPracticeCards(t) {
27596
+ let { group: c, courseId: u, emittedIds: d, cardsByTag: m, hierarchyConfigs: g, userTagElo: b, userGlobalElo: S, activeIds: C, seenIds: w } = t, T = c.practiceTagPatterns ?? [];
27597
+ if (T.length === 0) return [];
27598
+ let E = c.practiceMinCount ?? DEFAULT_PRACTICE_MIN_COUNT, D = c.maxPracticeCardsPerRun ?? DEFAULT_MAX_PRACTICE_PER_RUN, O = [...m.keys()].filter((t) => T.some((c) => matchesTagPattern(t, c)) && this.isUnlockedGatedSkill(t, g, b, S) && (b[t]?.count ?? 0) < E);
27599
+ if (O.length === 0) return [];
27600
+ let Or = this.findDiscoveredSupportCards({
27601
+ supportTags: O,
27602
+ cardsByTag: m,
27603
+ activeIds: C,
27604
+ seenIds: w,
27605
+ excludedIds: d,
27606
+ limit: D
27607
+ });
27608
+ if (Or.length === 0) return [];
27609
+ logger.info(`[Prescribed] Group '${c.id}' practice: ${O.length} unlocked under-practiced skill(s), emitting ${Or.length} drill card(s)`);
27610
+ let kr = [];
27611
+ for (let t of Or) d.add(t), kr.push({
27612
+ cardId: t,
27613
+ courseId: u,
27614
+ score: BASE_PRACTICE_SCORE,
27615
+ provenance: [{
27616
+ strategy: "prescribed",
27617
+ strategyName: this.strategyName || this.name,
27618
+ strategyId: this.strategyId || "NAVIGATION_STRATEGY-prescribed",
27619
+ action: "generated",
27620
+ score: BASE_PRACTICE_SCORE,
27621
+ reason: `mode=practice;group=${c.id};underPracticedSkills=${O.length};practiceTags=${O.slice(0, 8).join("|")}${O.length > 8 ? "|…" : ""};testversion=${PRESCRIBED_DEBUG_VERSION}`
27622
+ }]
27623
+ });
27624
+ return kr;
27625
+ }
27626
+ isUnlockedGatedSkill(t, c, u, d) {
27627
+ let m = c.map((c) => c.prerequisites[t]).filter((t) => Array.isArray(t) && t.length > 0);
27628
+ return m.length === 0 ? !1 : m.every((t) => t.every((t) => this.isPrerequisiteMet(t, u[t.tag], d)));
27629
+ }
27465
27630
  findSupportCardsByTags(t, c, u) {
27466
27631
  if (u.length === 0) return [];
27467
27632
  let d = t.supportCardIds ?? [], m = t.supportTagPatterns ?? [];
@@ -28041,7 +28206,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
28041
28206
  Pipeline: () => Pipeline,
28042
28207
  mergeHints: () => mergeHints2
28043
28208
  }), init_Pipeline = __esm({ "src/core/navigators/Pipeline.ts"() {
28044
- init_navigators(), init_logger(), init_orchestration(), init_PipelineDebugger(), VERBOSE_RESULTS = !0, Pipeline = class extends ContentNavigator {
28209
+ init_navigators(), init_logger(), init_orchestration(), init_PipelineDebugger(), init_diversityRerank(), VERBOSE_RESULTS = !0, Pipeline = class extends ContentNavigator {
28045
28210
  constructor(t, c, u, d) {
28046
28211
  super(), _defineProperty$2(this, "generator", void 0), _defineProperty$2(this, "filters", void 0), _defineProperty$2(this, "_cachedOrchestration", null), _defineProperty$2(this, "_tagCache", /* @__PURE__ */ new Map()), _defineProperty$2(this, "_ephemeralHints", null), this.generator = t, this.filters = c, this.user = u, this.course = d, d.getCourseConfig().then((t) => {
28047
28212
  logger.debug(`[pipeline] Crated pipeline for ${t.name}`);
@@ -28117,7 +28282,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
28117
28282
  }
28118
28283
  b = b.filter((t) => t.score > 0);
28119
28284
  let kr = this._ephemeralHints;
28120
- kr && (this._ephemeralHints = null, b = this.applyHints(b, kr, E)), b.sort((t, c) => c.score - t.score);
28285
+ kr && (this._ephemeralHints = null, b = this.applyHints(b, kr, E)), b = diversityRerank(b), b.sort((t, c) => c.score - t.score);
28121
28286
  let Ar = performance.now(), jr = b.slice(0, t);
28122
28287
  logger.info(`[Pipeline:timing] total=${(Ar - c).toFixed(0)}ms (context=${(d - c).toFixed(0)} generate=${(S - d).toFixed(0)} hydrate=${(T - S).toFixed(0)} filter=${(Ar - T).toFixed(0)})`);
28123
28288
  let Mr = jr.slice(0, 3).map((t) => t.score);
@@ -28353,6 +28518,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
28353
28518
  "./PipelineAssembler.ts": () => Promise.resolve().then(() => (init_PipelineAssembler(), PipelineAssembler_exports)),
28354
28519
  "./PipelineDebugger.ts": () => Promise.resolve().then(() => (init_PipelineDebugger(), PipelineDebugger_exports)),
28355
28520
  "./defaults.ts": () => Promise.resolve().then(() => (init_defaults(), defaults_exports)),
28521
+ "./diversityRerank.ts": () => Promise.resolve().then(() => (init_diversityRerank(), diversityRerank_exports)),
28356
28522
  "./filters/WeightedFilter.ts": () => Promise.resolve().then(() => (init_WeightedFilter(), WeightedFilter_exports)),
28357
28523
  "./filters/eloDistance.ts": () => Promise.resolve().then(() => (init_eloDistance(), eloDistance_exports)),
28358
28524
  "./filters/hierarchyDefinition.ts": () => Promise.resolve().then(() => (init_hierarchyDefinition(), hierarchyDefinition_exports)),
@@ -28373,9 +28539,12 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
28373
28539
  });
28374
28540
  } }), navigators_exports = {}, __export(navigators_exports, {
28375
28541
  ContentNavigator: () => ContentNavigator,
28542
+ DIVERSITY_FLOOR: () => DIVERSITY_FLOOR,
28543
+ DIVERSITY_STRENGTH: () => DIVERSITY_STRENGTH,
28376
28544
  NavigatorRole: () => NavigatorRole,
28377
28545
  NavigatorRoles: () => NavigatorRoles,
28378
28546
  Navigators: () => Navigators,
28547
+ diversityRerank: () => diversityRerank,
28379
28548
  getCardOrigin: () => getCardOrigin,
28380
28549
  getRegisteredNavigator: () => getRegisteredNavigator,
28381
28550
  getRegisteredNavigatorNames: () => getRegisteredNavigatorNames,
@@ -28388,7 +28557,7 @@ var import___vite_browser_external, import___vite_browser_external$1, import___v
28388
28557
  pipelineDebugAPI: () => pipelineDebugAPI,
28389
28558
  registerNavigator: () => registerNavigator
28390
28559
  }), init_navigators = __esm({ "src/core/navigators/index.ts"() {
28391
- init_PipelineDebugger(), init_logger(), init_(), init_2(), init_3(), navigatorRegistry = /* @__PURE__ */ new Map(), Navigators = /* @__PURE__ */ ((t) => (t.ELO = "elo", t.SRS = "srs", t.PRESCRIBED = "prescribed", t.HIERARCHY = "hierarchyDefinition", t.INTERFERENCE = "interferenceMitigator", t.RELATIVE_PRIORITY = "relativePriority", t.USER_TAG_PREFERENCE = "userTagPreference", t))(Navigators || {}), NavigatorRole = /* @__PURE__ */ ((t) => (t.GENERATOR = "generator", t.FILTER = "filter", t))(NavigatorRole || {}), NavigatorRoles = {
28560
+ init_diversityRerank(), init_PipelineDebugger(), init_logger(), init_(), init_2(), init_3(), navigatorRegistry = /* @__PURE__ */ new Map(), Navigators = /* @__PURE__ */ ((t) => (t.ELO = "elo", t.SRS = "srs", t.PRESCRIBED = "prescribed", t.HIERARCHY = "hierarchyDefinition", t.INTERFERENCE = "interferenceMitigator", t.RELATIVE_PRIORITY = "relativePriority", t.USER_TAG_PREFERENCE = "userTagPreference", t))(Navigators || {}), NavigatorRole = /* @__PURE__ */ ((t) => (t.GENERATOR = "generator", t.FILTER = "filter", t))(NavigatorRole || {}), NavigatorRoles = {
28392
28561
  elo: "generator",
28393
28562
  srs: "generator",
28394
28563
  prescribed: "generator",
@@ -31044,13 +31213,17 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
31044
31213
  this.seenCardIds.includes(t) || (this.seenCardIds.push(t), this.q.push(u));
31045
31214
  }
31046
31215
  }
31047
- mergeToFront(t, c) {
31048
- let u = 0, d = [];
31049
- for (let m of t) {
31050
- let t = c(m);
31051
- this.seenCardIds.includes(t) || (this.seenCardIds.push(t), d.push(m), u++);
31216
+ mergeToFront(t, c, u) {
31217
+ let d = 0, m = [];
31218
+ for (let g of t) {
31219
+ let t = c(g);
31220
+ if (!this.seenCardIds.includes(t)) this.seenCardIds.push(t), m.push(g), d++;
31221
+ else if (u?.has(t)) {
31222
+ let u = this.q.findIndex((u) => c(u) === t);
31223
+ u >= 0 && m.push(...this.q.splice(u, 1));
31224
+ }
31052
31225
  }
31053
- return this.q.unshift(...d), u;
31226
+ return this.q.unshift(...m), d;
31054
31227
  }
31055
31228
  get toString() {
31056
31229
  return `${typeof this.q[0]}:
@@ -31780,10 +31953,11 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
31780
31953
  "⠧",
31781
31954
  "⠇",
31782
31955
  "⠏"
31783
- ], spinnerFrame = 0, overlayEl = null, pollHandle = null, expanded = {
31956
+ ], spinnerFrame = 0, overlayEl = null, pollHandle = null, lastSnapshot = null, copyFlashUntil = 0, minified = !1, expanded = {
31784
31957
  reviewQ: !1,
31785
31958
  newQ: !1,
31786
- failedQ: !1
31959
+ failedQ: !1,
31960
+ drawn: !1
31787
31961
  }, activeSession = null, sessionHistory = [], MAX_HISTORY = 5, sessionDebugAPI = {
31788
31962
  get sessions() {
31789
31963
  return [...sessionHistory];
@@ -31847,7 +32021,7 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
31847
32021
  return this.newQ.toString + "\n" + this.reviewQ.toString + "\n" + this.failedQ.toString;
31848
32022
  }
31849
32023
  constructor(t, c, u, d, m, g) {
31850
- super(), _defineProperty$2(this, "_className", "SessionController"), _defineProperty$2(this, "services", void 0), _defineProperty$2(this, "srsService", void 0), _defineProperty$2(this, "eloService", void 0), _defineProperty$2(this, "hydrationService", void 0), _defineProperty$2(this, "mixer", void 0), _defineProperty$2(this, "dataLayer", void 0), _defineProperty$2(this, "courseNameCache", /* @__PURE__ */ new Map()), _defineProperty$2(this, "_defaultBatchLimit", 20), _defineProperty$2(this, "_initialReviewCap", 200), _defineProperty$2(this, "sources", void 0), _defineProperty$2(this, "_sessionRecord", []), _defineProperty$2(this, "_currentCard", null), _defineProperty$2(this, "reviewQ", new ItemQueue()), _defineProperty$2(this, "newQ", new ItemQueue()), _defineProperty$2(this, "failedQ", new ItemQueue()), _defineProperty$2(this, "_replanPromise", null), _defineProperty$2(this, "_activeReplanLabel", null), _defineProperty$2(this, "_wellIndicatedRemaining", 0), _defineProperty$2(this, "_suppressQualityReplan", !1), _defineProperty$2(this, "_minCardsGuarantee", 0), _defineProperty$2(this, "_sessionHints", null), _defineProperty$2(this, "_outcomeObservers", []), _defineProperty$2(this, "_sessionControls", null), _defineProperty$2(this, "startTime", void 0), _defineProperty$2(this, "endTime", void 0), _defineProperty$2(this, "_secondsRemaining", void 0), _defineProperty$2(this, "_intervalHandle", void 0), this.dataLayer = u, this.mixer = m || new QuotaRoundRobinMixer(), this.srsService = new SrsService(u.getUserDB()), this.eloService = new EloService(u, u.getUserDB()), this.hydrationService = new CardHydrationService(d, (t) => u.getCourseDB(t), () => this._getItemsToHydrate()), this.services = { response: new ResponseProcessor(this.srsService, this.eloService) }, this.sources = t, this.startTime = /* @__PURE__ */ new Date(), this._secondsRemaining = c, this.endTime = new Date(this.startTime.valueOf() + 1e3 * this._secondsRemaining), g?.defaultBatchLimit !== void 0 && (this._defaultBatchLimit = g.defaultBatchLimit), g?.initialReviewCap !== void 0 && (this._initialReviewCap = g.initialReviewCap), g?.outcomeObservers?.length && (this._outcomeObservers = [...g.outcomeObservers]), this.log(`Session constructed:
32024
+ super(), _defineProperty$2(this, "_className", "SessionController"), _defineProperty$2(this, "services", void 0), _defineProperty$2(this, "srsService", void 0), _defineProperty$2(this, "eloService", void 0), _defineProperty$2(this, "hydrationService", void 0), _defineProperty$2(this, "mixer", void 0), _defineProperty$2(this, "dataLayer", void 0), _defineProperty$2(this, "courseNameCache", /* @__PURE__ */ new Map()), _defineProperty$2(this, "_defaultBatchLimit", 20), _defineProperty$2(this, "_initialReviewCap", 200), _defineProperty$2(this, "sources", void 0), _defineProperty$2(this, "_sessionRecord", []), _defineProperty$2(this, "_currentCard", null), _defineProperty$2(this, "reviewQ", new ItemQueue()), _defineProperty$2(this, "newQ", new ItemQueue()), _defineProperty$2(this, "failedQ", new ItemQueue()), _defineProperty$2(this, "_replanPromise", null), _defineProperty$2(this, "_activeReplanLabel", null), _defineProperty$2(this, "_wellIndicatedRemaining", 0), _defineProperty$2(this, "_suppressQualityReplan", !1), _defineProperty$2(this, "_minCardsGuarantee", 0), _defineProperty$2(this, "_sessionHints", null), _defineProperty$2(this, "_servedCardIds", /* @__PURE__ */ new Set()), _defineProperty$2(this, "_outcomeObservers", []), _defineProperty$2(this, "_sessionControls", null), _defineProperty$2(this, "startTime", void 0), _defineProperty$2(this, "endTime", void 0), _defineProperty$2(this, "_secondsRemaining", void 0), _defineProperty$2(this, "_intervalHandle", void 0), this.dataLayer = u, this.mixer = m || new QuotaRoundRobinMixer(), this.srsService = new SrsService(u.getUserDB()), this.eloService = new EloService(u, u.getUserDB()), this.hydrationService = new CardHydrationService(d, (t) => u.getCourseDB(t), () => this._getItemsToHydrate()), this.services = { response: new ResponseProcessor(this.srsService, this.eloService) }, this.sources = t, this.startTime = /* @__PURE__ */ new Date(), this._secondsRemaining = c, this.endTime = new Date(this.startTime.valueOf() + 1e3 * this._secondsRemaining), g?.defaultBatchLimit !== void 0 && (this._defaultBatchLimit = g.defaultBatchLimit), g?.initialReviewCap !== void 0 && (this._initialReviewCap = g.initialReviewCap), g?.outcomeObservers?.length && (this._outcomeObservers = [...g.outcomeObservers]), this.log(`Session constructed:
31851
32025
  startTime: ${this.startTime}
31852
32026
  endTime: ${this.endTime}
31853
32027
  defaultBatchLimit: ${this._defaultBatchLimit}
@@ -31896,14 +32070,14 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
31896
32070
  this._replanPromise = m, await d;
31897
32071
  }
31898
32072
  _replanHasIntent(t) {
31899
- return !!(t.limit !== void 0 || t.minFollowUpCards !== void 0 || t.mode && t.mode !== "replace" || t.hints && Object.keys(t.hints).length > 0 || t.sessionHints !== void 0);
32073
+ return !!(t.limit !== void 0 || t.minFollowUpCards !== void 0 || t.mode && t.mode !== "replace" || t.hints && Object.keys(t.hints).length > 0 || t.sessionHints !== void 0 || t.mergeSessionHints !== void 0);
31900
32074
  }
31901
32075
  async _runReplan(t) {
31902
32076
  this._activeReplanLabel = t.label ?? "(auto)", t.hints || (t.hints = {});
31903
32077
  let c = t.hints, u = new Set(c.excludeCards ?? []);
31904
32078
  this._currentCard?.item.cardID && u.add(this._currentCard.item.cardID);
31905
32079
  for (let t of this._sessionRecord) u.add(t.card.card_id);
31906
- this.newQ.length > 0 && u.add(this.newQ.peek(0).cardID), c.excludeCards = [...u], t.sessionHints !== void 0 && (this._sessionHints = t.sessionHints, this.log(`[Replan] Session hints ${t.sessionHints ? "set" : "cleared"}: ${JSON.stringify(t.sessionHints)}`)), this._applyHintsToSources(t.hints, t.label);
32080
+ this.newQ.length > 0 && u.add(this.newQ.peek(0).cardID), c.excludeCards = [...u], t.sessionHints !== void 0 && (this._sessionHints = t.sessionHints, this.log(`[Replan] Session hints ${t.sessionHints ? "set" : "cleared"}: ${JSON.stringify(t.sessionHints)}`)), t.mergeSessionHints !== void 0 && (this._sessionHints = mergeHints2([this._sessionHints, t.mergeSessionHints]) ?? null, this.log(`[Replan] Session hints merged: ${JSON.stringify(this._sessionHints)}`)), this._applyHintsToSources(t.hints, t.label);
31907
32081
  let d = t.label ? ` [${t.label}]` : "";
31908
32082
  this.log(`Mid-session replan requested${d} (limit: ${t.limit ?? "default"}, mode: ${t.mode ?? "replace"}${t.hints ? ", with hints" : ""})`), t.minFollowUpCards !== void 0 && t.minFollowUpCards > 0 && (this._minCardsGuarantee = Math.max(this._minCardsGuarantee, t.minFollowUpCards), this.log(`[Replan] Card guarantee set to ${this._minCardsGuarantee}`)), await this._executeReplan(t);
31909
32083
  }
@@ -31922,7 +32096,16 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
31922
32096
  dequeueCount: t.dequeueCount,
31923
32097
  cards: c
31924
32098
  };
31925
- };
32099
+ }, t = this._sessionRecord.map((t) => {
32100
+ let c = t.records[t.records.length - 1];
32101
+ return {
32102
+ cardID: t.item.cardID,
32103
+ status: t.item.status,
32104
+ attempts: t.records.length,
32105
+ correct: c && isQuestionRecord(c) ? c.isCorrect : null,
32106
+ timeSpentMs: t.records.reduce((t, c) => t + c.timeSpent, 0)
32107
+ };
32108
+ });
31926
32109
  return {
31927
32110
  secondsRemaining: this.secondsRemaining,
31928
32111
  hasCardGuarantee: this.hasCardGuarantee,
@@ -31934,7 +32117,8 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
31934
32117
  replanLabel: this._activeReplanLabel,
31935
32118
  reviewQ: describe(this.reviewQ),
31936
32119
  newQ: describe(this.newQ),
31937
- failedQ: describe(this.failedQ)
32120
+ failedQ: describe(this.failedQ),
32121
+ drawnCards: t
31938
32122
  };
31939
32123
  }
31940
32124
  mergeSessionHints(t) {
@@ -32082,9 +32266,9 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
32082
32266
  }));
32083
32267
  let C = S.map((t) => this.courseNameCache.get(t)), w = this.mixer instanceof QuotaRoundRobinMixer ? Math.ceil(m * this.sources.length / g.length) : void 0;
32084
32268
  captureMixerRun(this.mixer.constructor.name, g, S, C, m * this.sources.length, w, b);
32085
- let T = b.filter((t) => getCardOrigin(t) === "review").slice(0, this._initialReviewCap), E = b.filter((t) => getCardOrigin(t) === "new").slice(0, d);
32269
+ let T = b.filter((t) => getCardOrigin(t) === "review").slice(0, this._initialReviewCap), E = b.filter((t) => getCardOrigin(t) === "new" && !this._servedCardIds.has(t.cardId)), D = E.filter((t) => t.score === Infinity), O = E.filter((t) => t.score !== Infinity), Or = [...D, ...O.slice(0, Math.max(0, d - D.length))], kr = new Set(D.map((t) => t.cardId));
32086
32270
  logger.debug(`[reviews] got ${T.length} reviews from mixer`);
32087
- let D = c ? "Replan content:\n" : "Mixed content session created with:\n";
32271
+ let Ar = c ? "Replan content:\n" : "Mixed content session created with:\n";
32088
32272
  if (!c) for (let t of T) {
32089
32273
  let c = {
32090
32274
  cardID: t.cardId,
@@ -32094,11 +32278,11 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
32094
32278
  reviewID: t.reviewID,
32095
32279
  status: "review"
32096
32280
  };
32097
- this.reviewQ.add(c, c.cardID), D += `Review: ${t.courseId}::${t.cardId} (score: ${t.score.toFixed(2)})
32281
+ this.reviewQ.add(c, c.cardID), Ar += `Review: ${t.courseId}::${t.cardId} (score: ${t.score.toFixed(2)})
32098
32282
  `;
32099
32283
  }
32100
- let O = E.filter((t) => t.score >= _SessionController.WELL_INDICATED_SCORE).length, Or = [];
32101
- for (let t of E) {
32284
+ let jr = Or.filter((t) => t.score >= _SessionController.WELL_INDICATED_SCORE).length, Mr = [];
32285
+ for (let t of Or) {
32102
32286
  let c = {
32103
32287
  cardID: t.cardId,
32104
32288
  courseID: t.courseId,
@@ -32106,16 +32290,16 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
32106
32290
  contentSourceID: t.courseId,
32107
32291
  status: "new"
32108
32292
  };
32109
- Or.push(c), D += `New: ${t.courseId}::${t.cardId} (score: ${t.score.toFixed(2)})
32293
+ Mr.push(c), Ar += `New: ${t.courseId}::${t.cardId} (score: ${t.score.toFixed(2)})
32110
32294
  `;
32111
32295
  }
32112
32296
  if (u) {
32113
- let t = this.newQ.mergeToFront(Or, (t) => t.cardID);
32114
- D += `Additive merge: ${t} new cards added to front of newQ
32297
+ let t = this.newQ.mergeToFront(Mr, (t) => t.cardID, kr);
32298
+ Ar += `Additive merge: ${t} new cards added to front of newQ
32115
32299
  `;
32116
- } else if (c) this.newQ.replaceAll(Or, (t) => t.cardID);
32117
- else for (let t of Or) this.newQ.add(t, t.cardID);
32118
- return this.log(D), O;
32300
+ } else if (c) this.newQ.replaceAll(Mr, (t) => t.cardID);
32301
+ else for (let t of Mr) this.newQ.add(t, t.cardID);
32302
+ return this.log(Ar), jr;
32119
32303
  }
32120
32304
  _getItemsToHydrate() {
32121
32305
  let t = [], c = 2;
@@ -32136,7 +32320,10 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
32136
32320
  if (this.dismissCurrentCard(t), 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) {
32137
32321
  this._suppressQualityReplan = !1;
32138
32322
  let t = this.reviewQ.length + this.failedQ.length;
32139
- this.log(`[AutoReplan:depletion] newQ has ${this.newQ.length} card(s) (${t} in other queues) with ${this._secondsRemaining}s remaining. Triggering background replan.`), this.requestReplan({ label: "auto:depletion" });
32323
+ this.log(`[AutoReplan:depletion] newQ has ${this.newQ.length} card(s) (${t} in other queues) with ${this._secondsRemaining}s remaining. Triggering background replan.`), this.requestReplan({
32324
+ label: "auto:depletion",
32325
+ mode: "merge"
32326
+ });
32140
32327
  }
32141
32328
  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;
32142
32329
  let c = 3, u = 250, d = 0;
@@ -32186,7 +32373,16 @@ ${c.id}:`), logger.info(JSON.stringify(c.doc, null, 2));
32186
32373
  } else (t === "dismiss-error" || t === "dismiss-failed") && this.hydrationService.removeCard(this._currentCard.item.cardID);
32187
32374
  }
32188
32375
  removeItemFromQueue(t) {
32189
- this.reviewQ.peek(0)?.cardID === t.cardID ? this.reviewQ.dequeue((t) => t.cardID) : this.newQ.peek(0)?.cardID === t.cardID ? (this.newQ.dequeue((t) => t.cardID), this._wellIndicatedRemaining > 0 && this._wellIndicatedRemaining--) : this.failedQ.peek(0)?.cardID === t.cardID && this.failedQ.dequeue((t) => t.cardID);
32376
+ this._clearDurableRequirement(t.cardID), this._servedCardIds.add(t.cardID), this.reviewQ.peek(0)?.cardID === t.cardID ? this.reviewQ.dequeue((t) => t.cardID) : this.newQ.peek(0)?.cardID === t.cardID ? (this.newQ.dequeue((t) => t.cardID), this._wellIndicatedRemaining > 0 && this._wellIndicatedRemaining--) : this.failedQ.peek(0)?.cardID === t.cardID && this.failedQ.dequeue((t) => t.cardID);
32377
+ }
32378
+ _clearDurableRequirement(t) {
32379
+ let c = this._sessionHints?.requireCards;
32380
+ if (!c || c.length === 0) return;
32381
+ let u = c.filter((c) => c !== t);
32382
+ u.length !== c.length && (this._sessionHints = {
32383
+ ...this._sessionHints,
32384
+ requireCards: u.length > 0 ? u : void 0
32385
+ }, this.log(`[Replan] Durable requirement satisfied & cleared on draw: ${t}`));
32190
32386
  }
32191
32387
  async endSession() {
32192
32388
  if (!this._sessionRecord || this._sessionRecord.length === 0) return;
@@ -41851,7 +42047,7 @@ function _sfc_render$27(t, c, u, d, m, g) {
41851
42047
  }, 8, ["loading"]);
41852
42048
  }
41853
42049
  var Ee$1, De$1, Oe$1, ke$1, Ae$1, je$1, Me$1, Ne$1, Pe$1, Fe$1, Ie$1, Le$1, Z$2, Re$1, ze$1, Be$1, Ve$1, He$1, Ue$1, We$1, Ge$1, Ke$1, qe$1, Je$1, Ye$1, Xe$1, Ze$1, Qe$1, $e$1, et$1, tt$1, nt$1, rt$1, it$1, at$1, Displayable, Question, getApiBase, ot$1, st$1, ct$1, Q$1, lt$1, ut$1, dt$1, ft$1, pt$1, mt$1, ht$1, gt$1, _t, vt$1, yt, bt, xt, St, Ct, wt, Tt, Et, Dt, Ot, kt, At, jt, Mt, Nt, Pt, Ft, It, Lt, Rt, zt, Bt, Vt, Ht, Ut, Wt, Gt, Kt, qt, Jt, Yt, Xt, Zt, Qt, $t, en, tn, nn, rn, an, on, sn, cn, ln, un, dn, fn, pn, mn, hn, gn, _n, vn, yn, useAuthStore, useConfigStore, bn, xn, Sn, Cn, wn, Tn, En, Dn, On, kn, An, jn, Mn, Nn, Pn, Fn, In, Ln, Rn, zn, Bn, Vn, Hn, Un, Wn, Gn, Kn, qn, Jn, Yn, Xn, Zn, Qn, $n, er, tr, nr, rr, ir, ar, or, sr, cr, lr, ur, dr, fr, pr, mr, hr, gr, _r, vr, $$2, yr, br, xr, Sr, Cr, wr, Tr, Er, Dr, init_common_ui_es = __esmMin((() => {
41854
- init_MarkdownRenderer_DoVbFpA6(), init_vue_runtime_esm_bundler(), init_moment(), init_dist$1(), init_dist(), init_pinia(), init_vue_router(), init_vue_tags_input(), Ee$1 = /* @__PURE__ */ defineComponent({
42050
+ init_MarkdownRenderer_kStoDRNE(), init_vue_runtime_esm_bundler(), init_moment(), init_dist$1(), init_dist(), init_pinia(), init_vue_router(), init_vue_tags_input(), Ee$1 = /* @__PURE__ */ defineComponent({
41855
42051
  name: "HeatMap",
41856
42052
  props: {
41857
42053
  activityRecords: {
@@ -43231,7 +43427,7 @@ var Ee$1, De$1, Oe$1, ke$1, Ae$1, je$1, Me$1, Ne$1, Pe$1, Fe$1, Ie$1, Le$1, Z$2,
43231
43427
  class: "card-transition-container"
43232
43428
  }, on = { key: 0 }, __name(_sfc_render$15, "_sfc_render"), sn = /* @__PURE__ */ F$3(Qt, [["render", _sfc_render$15], ["__scopeId", "data-v-de7119e9"]]), cn = /* @__PURE__ */ defineComponent({
43233
43429
  name: "MultipleChoiceOption",
43234
- components: { MarkdownRenderer: /* @__PURE__ */ defineAsyncComponent(() => Promise.resolve().then(() => (init_MarkdownRenderer_DoVbFpA6(), MarkdownRenderer_DoVbFpA6_exports)).then((t) => t.n)) },
43430
+ components: { MarkdownRenderer: /* @__PURE__ */ defineAsyncComponent(() => Promise.resolve().then(() => (init_MarkdownRenderer_kStoDRNE(), MarkdownRenderer_kStoDRNE_exports)).then((t) => t.n)) },
43235
43431
  props: {
43236
43432
  content: {
43237
43433
  type: String,