@vue-skuilder/db 0.1.34 → 0.1.36
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/core/index.d.cts +46 -0
- package/dist/core/index.d.ts +46 -0
- package/dist/core/index.js +163 -12
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +163 -12
- package/dist/core/index.mjs.map +1 -1
- package/dist/impl/couch/index.js +163 -12
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +163 -12
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.js +163 -12
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +163 -12
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.cts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +232 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/core/navigators/Pipeline.ts +5 -2
- package/src/core/navigators/PipelineDebugger.ts +238 -8
- package/src/study/SessionController.ts +88 -11
- package/src/study/SessionDebugger.ts +8 -0
|
@@ -626,6 +626,7 @@ var PipelineDebugger_exports = {};
|
|
|
626
626
|
__export(PipelineDebugger_exports, {
|
|
627
627
|
buildRunReport: () => buildRunReport,
|
|
628
628
|
captureRun: () => captureRun,
|
|
629
|
+
clearRunHistory: () => clearRunHistory,
|
|
629
630
|
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
630
631
|
pipelineDebugAPI: () => pipelineDebugAPI,
|
|
631
632
|
registerPipelineForDebug: () => registerPipelineForDebug
|
|
@@ -633,6 +634,9 @@ __export(PipelineDebugger_exports, {
|
|
|
633
634
|
function registerPipelineForDebug(pipeline) {
|
|
634
635
|
_activePipeline = pipeline;
|
|
635
636
|
}
|
|
637
|
+
function clearRunHistory() {
|
|
638
|
+
runHistory.length = 0;
|
|
639
|
+
}
|
|
636
640
|
function getOrigin(card) {
|
|
637
641
|
const firstEntry = card.provenance[0];
|
|
638
642
|
if (!firstEntry) return "unknown";
|
|
@@ -661,7 +665,7 @@ function parseCardElo(provenance) {
|
|
|
661
665
|
}
|
|
662
666
|
function buildRunReport(courseId, courseName, generatorName, generators, generatedCount, filters, allCards, selectedCards, userElo, hints) {
|
|
663
667
|
const selectedIds = new Set(selectedCards.map((c) => c.cardId));
|
|
664
|
-
const
|
|
668
|
+
const toReport = (card) => ({
|
|
665
669
|
cardId: card.cardId,
|
|
666
670
|
courseId: card.courseId,
|
|
667
671
|
origin: getOrigin(card),
|
|
@@ -671,7 +675,47 @@ function buildRunReport(courseId, courseName, generatorName, generators, generat
|
|
|
671
675
|
provenance: card.provenance,
|
|
672
676
|
tags: card.tags,
|
|
673
677
|
selected: selectedIds.has(card.cardId)
|
|
674
|
-
})
|
|
678
|
+
});
|
|
679
|
+
const selectedReported = [];
|
|
680
|
+
const nearMissReported = [];
|
|
681
|
+
const discardedTailCards = [];
|
|
682
|
+
let nonSelectedSeen = 0;
|
|
683
|
+
for (const card of allCards) {
|
|
684
|
+
if (selectedIds.has(card.cardId)) {
|
|
685
|
+
selectedReported.push(toReport(card));
|
|
686
|
+
} else if (nonSelectedSeen < DISCARDED_KEEP_TOP) {
|
|
687
|
+
nearMissReported.push(toReport(card));
|
|
688
|
+
nonSelectedSeen++;
|
|
689
|
+
} else {
|
|
690
|
+
discardedTailCards.push(card);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
const cards = [...selectedReported, ...nearMissReported];
|
|
694
|
+
let discardedTail;
|
|
695
|
+
if (discardedTailCards.length > 0) {
|
|
696
|
+
let scoreMin = Infinity;
|
|
697
|
+
let scoreMax = -Infinity;
|
|
698
|
+
let eloMin = Infinity;
|
|
699
|
+
let eloMax = -Infinity;
|
|
700
|
+
let eloSeen = false;
|
|
701
|
+
for (const c of discardedTailCards) {
|
|
702
|
+
if (c.score < scoreMin) scoreMin = c.score;
|
|
703
|
+
if (c.score > scoreMax) scoreMax = c.score;
|
|
704
|
+
const elo = parseCardElo(c.provenance);
|
|
705
|
+
if (elo !== void 0) {
|
|
706
|
+
eloSeen = true;
|
|
707
|
+
if (elo < eloMin) eloMin = elo;
|
|
708
|
+
if (elo > eloMax) eloMax = elo;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
const eloFragment = eloSeen ? `, ELO ${eloMin}\u2013${eloMax}` : "";
|
|
712
|
+
discardedTail = {
|
|
713
|
+
count: discardedTailCards.length,
|
|
714
|
+
scoreRange: [scoreMin, scoreMax],
|
|
715
|
+
eloRange: eloSeen ? [eloMin, eloMax] : void 0,
|
|
716
|
+
note: `${discardedTailCards.length} additional candidate(s) scored below the top ${DISCARDED_KEEP_TOP} near-misses and were not retained (score ${scoreMin.toExponential(2)}\u2013${scoreMax.toExponential(2)}${eloFragment}). Likely ELO-window pull remnants filtered out by hierarchy/lesson/priority gates.`
|
|
717
|
+
};
|
|
718
|
+
}
|
|
675
719
|
const reviewsSelected = selectedCards.filter((c) => getOrigin(c) === "review").length;
|
|
676
720
|
const newSelected = selectedCards.filter((c) => getOrigin(c) === "new").length;
|
|
677
721
|
return {
|
|
@@ -686,7 +730,8 @@ function buildRunReport(courseId, courseName, generatorName, generators, generat
|
|
|
686
730
|
finalCount: selectedCards.length,
|
|
687
731
|
reviewsSelected,
|
|
688
732
|
newSelected,
|
|
689
|
-
cards
|
|
733
|
+
cards,
|
|
734
|
+
discardedTail
|
|
690
735
|
};
|
|
691
736
|
}
|
|
692
737
|
function formatProvenance(provenance) {
|
|
@@ -722,6 +767,44 @@ function printRunSummary(run) {
|
|
|
722
767
|
);
|
|
723
768
|
console.groupEnd();
|
|
724
769
|
}
|
|
770
|
+
function escapeHtml(s) {
|
|
771
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
772
|
+
}
|
|
773
|
+
function escapeAttr(s) {
|
|
774
|
+
return escapeHtml(s).replace(/"/g, """);
|
|
775
|
+
}
|
|
776
|
+
function copyTextToClipboard(text, btn) {
|
|
777
|
+
const done = () => {
|
|
778
|
+
if (!btn) return;
|
|
779
|
+
const orig = btn.textContent ?? "Copy";
|
|
780
|
+
btn.textContent = "Copied!";
|
|
781
|
+
btn.classList.add("copied");
|
|
782
|
+
setTimeout(() => {
|
|
783
|
+
btn.textContent = orig;
|
|
784
|
+
btn.classList.remove("copied");
|
|
785
|
+
}, 1200);
|
|
786
|
+
};
|
|
787
|
+
const fallback = () => {
|
|
788
|
+
const ta = document.createElement("textarea");
|
|
789
|
+
ta.value = text;
|
|
790
|
+
ta.style.position = "fixed";
|
|
791
|
+
ta.style.opacity = "0";
|
|
792
|
+
document.body.appendChild(ta);
|
|
793
|
+
ta.select();
|
|
794
|
+
try {
|
|
795
|
+
document.execCommand("copy");
|
|
796
|
+
} catch (e) {
|
|
797
|
+
logger.warn(`[Pipeline Debug] Copy failed: ${e}`);
|
|
798
|
+
}
|
|
799
|
+
document.body.removeChild(ta);
|
|
800
|
+
done();
|
|
801
|
+
};
|
|
802
|
+
if (navigator.clipboard?.writeText) {
|
|
803
|
+
navigator.clipboard.writeText(text).then(done).catch(fallback);
|
|
804
|
+
} else {
|
|
805
|
+
fallback();
|
|
806
|
+
}
|
|
807
|
+
}
|
|
725
808
|
function renderUI() {
|
|
726
809
|
if (!_uiContainer) return;
|
|
727
810
|
const runs = runHistory;
|
|
@@ -780,6 +863,13 @@ function renderUI() {
|
|
|
780
863
|
#sk-pipeline-debugger .close-btn { background: #dc3545; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; }
|
|
781
864
|
#sk-pipeline-debugger .search-box { margin-bottom: 1rem; width: 100%; padding: 0.5rem; border: 1px solid #ced4da; border-radius: 4px; }
|
|
782
865
|
#sk-pipeline-debugger .provenance { font-size: 12px; color: #666; margin-top: 0.25rem; white-space: pre-wrap; font-family: monospace; background: #f8f9fa; padding: 0.5rem; border-radius: 4px; }
|
|
866
|
+
#sk-pipeline-debugger .run-label { display: inline-block; margin-top: 0.25rem; padding: 0.1rem 0.4rem; background: #fff3cd; color: #664d03; border-radius: 3px; font-family: monospace; font-size: 11px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
|
|
867
|
+
#sk-pipeline-debugger .label-banner { display: inline-block; padding: 0.25rem 0.6rem; background: #fff3cd; color: #664d03; border-radius: 4px; font-family: monospace; font-size: 13px; margin: 0 0 0.75rem 0; }
|
|
868
|
+
#sk-pipeline-debugger .copy-btn { background: #0d6efd; color: white; border: none; padding: 0.25rem 0.6rem; border-radius: 3px; cursor: pointer; font-size: 12px; margin-left: 0.5rem; }
|
|
869
|
+
#sk-pipeline-debugger .copy-btn:hover { background: #0b5ed7; }
|
|
870
|
+
#sk-pipeline-debugger .copy-btn.copied { background: #198754; }
|
|
871
|
+
#sk-pipeline-debugger .section-head { display: flex; align-items: center; justify-content: space-between; margin-top: 1rem; }
|
|
872
|
+
#sk-pipeline-debugger .section-head h3 { margin: 0; }
|
|
783
873
|
`;
|
|
784
874
|
const runListHtml = runs.length === 0 ? '<div style="padding: 1rem;">No runs captured yet.</div>' : runs.map(
|
|
785
875
|
(r, i) => `
|
|
@@ -787,6 +877,7 @@ function renderUI() {
|
|
|
787
877
|
<strong>${r.timestamp.toLocaleTimeString()}</strong><br/>
|
|
788
878
|
<small>${r.courseName || r.courseId.slice(0, 8)}</small><br/>
|
|
789
879
|
<small>${r.finalCount} cards selected</small>
|
|
880
|
+
${r.hints?._label ? `<br/><span class="run-label" title="${escapeAttr(r.hints._label)}">${escapeHtml(r.hints._label)}</span>` : ""}
|
|
790
881
|
</div>
|
|
791
882
|
`
|
|
792
883
|
).join("");
|
|
@@ -795,11 +886,13 @@ function renderUI() {
|
|
|
795
886
|
const filteredCards = selectedRun.cards.filter(
|
|
796
887
|
(c) => !_cardSearchQuery || c.cardId.toLowerCase().includes(_cardSearchQuery.toLowerCase())
|
|
797
888
|
);
|
|
889
|
+
const labelText = selectedRun.hints?._label ?? "(no label)";
|
|
798
890
|
detailsHtml = `
|
|
799
891
|
<h2>Run: ${selectedRun.runId}</h2>
|
|
892
|
+
<div class="label-banner" title="${escapeAttr(labelText)}">${escapeHtml(labelText)}</div>
|
|
800
893
|
<p>
|
|
801
|
-
<strong>Time:</strong> ${selectedRun.timestamp.toLocaleString()} |
|
|
802
|
-
<strong>Course:</strong> ${selectedRun.courseName || selectedRun.courseId} |
|
|
894
|
+
<strong>Time:</strong> ${selectedRun.timestamp.toLocaleString()} |
|
|
895
|
+
<strong>Course:</strong> ${selectedRun.courseName || selectedRun.courseId} |
|
|
803
896
|
<strong>User ELO:</strong> ${selectedRun.userElo ?? "unknown"}
|
|
804
897
|
</p>
|
|
805
898
|
|
|
@@ -814,7 +907,10 @@ function renderUI() {
|
|
|
814
907
|
</table>
|
|
815
908
|
|
|
816
909
|
${selectedRun.hints ? `
|
|
817
|
-
<
|
|
910
|
+
<div class="section-head">
|
|
911
|
+
<h3>Ephemeral Hints</h3>
|
|
912
|
+
<button class="copy-btn" onclick="window.skuilder.pipeline._copyConfig('${selectedRun.runId}', this)">Copy config</button>
|
|
913
|
+
</div>
|
|
818
914
|
<table>
|
|
819
915
|
${selectedRun.hints._label ? `<tr><th>Label</th><td>${selectedRun.hints._label}</td></tr>` : ""}
|
|
820
916
|
${selectedRun.hints.boostTags ? `<tr><th>Boost Tags</th><td><pre style="margin:0">${JSON.stringify(selectedRun.hints.boostTags, null, 2)}</pre></td></tr>` : ""}
|
|
@@ -838,7 +934,10 @@ function renderUI() {
|
|
|
838
934
|
</tbody>
|
|
839
935
|
</table>
|
|
840
936
|
|
|
841
|
-
<
|
|
937
|
+
<div class="section-head">
|
|
938
|
+
<h3>Cards (${selectedRun.finalCount} selected / ${selectedRun.cards.length} total)</h3>
|
|
939
|
+
<button class="copy-btn" onclick="window.skuilder.pipeline._copyResults('${selectedRun.runId}', this)">Copy results</button>
|
|
940
|
+
</div>
|
|
842
941
|
<input type="text" class="search-box" placeholder="Search Card ID..." value="${_cardSearchQuery}" oninput="window.skuilder.pipeline._setSearch(this.value)">
|
|
843
942
|
|
|
844
943
|
<table>
|
|
@@ -884,7 +983,7 @@ function mountPipelineDebugger() {
|
|
|
884
983
|
win.skuilder = win.skuilder || {};
|
|
885
984
|
win.skuilder.pipeline = pipelineDebugAPI;
|
|
886
985
|
}
|
|
887
|
-
var _activePipeline, MAX_RUNS, runHistory, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI;
|
|
986
|
+
var _activePipeline, MAX_RUNS, runHistory, DISCARDED_KEEP_TOP, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI;
|
|
888
987
|
var init_PipelineDebugger = __esm({
|
|
889
988
|
"src/core/navigators/PipelineDebugger.ts"() {
|
|
890
989
|
"use strict";
|
|
@@ -893,6 +992,7 @@ var init_PipelineDebugger = __esm({
|
|
|
893
992
|
_activePipeline = null;
|
|
894
993
|
MAX_RUNS = 10;
|
|
895
994
|
runHistory = [];
|
|
995
|
+
DISCARDED_KEEP_TOP = 25;
|
|
896
996
|
_uiContainer = null;
|
|
897
997
|
_selectedRunIndex = null;
|
|
898
998
|
_cardSearchQuery = "";
|
|
@@ -957,7 +1057,14 @@ var init_PipelineDebugger = __esm({
|
|
|
957
1057
|
return;
|
|
958
1058
|
}
|
|
959
1059
|
}
|
|
960
|
-
|
|
1060
|
+
const runsWithTails = runHistory.filter((r) => r.discardedTail && r.discardedTail.count > 0);
|
|
1061
|
+
if (runsWithTails.length > 0) {
|
|
1062
|
+
logger.info(
|
|
1063
|
+
`[Pipeline Debug] Card '${cardId}' not found in retained cards. ${runsWithTails.length} run(s) have discarded tails that were not retained \u2014 the card may have been a low-score candidate. See run.discardedTail for ranges.`
|
|
1064
|
+
);
|
|
1065
|
+
} else {
|
|
1066
|
+
logger.info(`[Pipeline Debug] Card '${cardId}' not found in recent runs.`);
|
|
1067
|
+
}
|
|
961
1068
|
},
|
|
962
1069
|
/**
|
|
963
1070
|
* Explain why reviews may or may not have been selected.
|
|
@@ -1262,6 +1369,50 @@ var init_PipelineDebugger = __esm({
|
|
|
1262
1369
|
_cardSearchQuery = query;
|
|
1263
1370
|
renderUI();
|
|
1264
1371
|
},
|
|
1372
|
+
/**
|
|
1373
|
+
* Internal UI helpers
|
|
1374
|
+
* @internal
|
|
1375
|
+
*/
|
|
1376
|
+
_copyConfig(runId, btn) {
|
|
1377
|
+
const run = runHistory.find((r) => r.runId === runId);
|
|
1378
|
+
if (!run) return;
|
|
1379
|
+
const payload = {
|
|
1380
|
+
runId: run.runId,
|
|
1381
|
+
timestamp: run.timestamp.toISOString(),
|
|
1382
|
+
courseId: run.courseId,
|
|
1383
|
+
courseName: run.courseName,
|
|
1384
|
+
hints: run.hints ?? null
|
|
1385
|
+
};
|
|
1386
|
+
copyTextToClipboard(JSON.stringify(payload, null, 2), btn);
|
|
1387
|
+
},
|
|
1388
|
+
/**
|
|
1389
|
+
* Internal UI helpers
|
|
1390
|
+
* @internal
|
|
1391
|
+
*
|
|
1392
|
+
* Copies an "abridged" view of results: just the selected cards with their
|
|
1393
|
+
* generator, origin, final score, and the top provenance reason. Designed
|
|
1394
|
+
* for pasting into bug reports without flooding with full provenance.
|
|
1395
|
+
*/
|
|
1396
|
+
_copyResults(runId, btn) {
|
|
1397
|
+
const run = runHistory.find((r) => r.runId === runId);
|
|
1398
|
+
if (!run) return;
|
|
1399
|
+
const selected = run.cards.filter((c) => c.selected).sort((a, b) => b.finalScore - a.finalScore).map((c) => ({
|
|
1400
|
+
cardId: c.cardId,
|
|
1401
|
+
generator: c.generator,
|
|
1402
|
+
origin: c.origin,
|
|
1403
|
+
score: Number(c.finalScore.toFixed(3)),
|
|
1404
|
+
topReason: c.provenance[0]?.reason ?? ""
|
|
1405
|
+
}));
|
|
1406
|
+
const payload = {
|
|
1407
|
+
runId: run.runId,
|
|
1408
|
+
label: run.hints?._label ?? null,
|
|
1409
|
+
finalCount: run.finalCount,
|
|
1410
|
+
newSelected: run.newSelected,
|
|
1411
|
+
reviewsSelected: run.reviewsSelected,
|
|
1412
|
+
selected
|
|
1413
|
+
};
|
|
1414
|
+
copyTextToClipboard(JSON.stringify(payload, null, 2), btn);
|
|
1415
|
+
},
|
|
1265
1416
|
/**
|
|
1266
1417
|
* Show help.
|
|
1267
1418
|
*/
|
|
@@ -5372,12 +5523,12 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5372
5523
|
async getWeightedCards(limit) {
|
|
5373
5524
|
const u = await this._getCurrentUser();
|
|
5374
5525
|
try {
|
|
5375
|
-
const
|
|
5526
|
+
const navigator2 = await this.createNavigator(u);
|
|
5376
5527
|
if (this._pendingHints) {
|
|
5377
|
-
|
|
5528
|
+
navigator2.setEphemeralHints(this._pendingHints);
|
|
5378
5529
|
this._pendingHints = null;
|
|
5379
5530
|
}
|
|
5380
|
-
return
|
|
5531
|
+
return navigator2.getWeightedCards(limit);
|
|
5381
5532
|
} catch (e) {
|
|
5382
5533
|
logger.error(`[courseDB] Error getting weighted cards: ${e}`);
|
|
5383
5534
|
throw e;
|