@vue-skuilder/db 0.1.35 → 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 +229 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +229 -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 +74 -11
- package/src/study/SessionDebugger.ts +8 -0
package/dist/core/index.mjs
CHANGED
|
@@ -701,6 +701,7 @@ var PipelineDebugger_exports = {};
|
|
|
701
701
|
__export(PipelineDebugger_exports, {
|
|
702
702
|
buildRunReport: () => buildRunReport,
|
|
703
703
|
captureRun: () => captureRun,
|
|
704
|
+
clearRunHistory: () => clearRunHistory,
|
|
704
705
|
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
705
706
|
pipelineDebugAPI: () => pipelineDebugAPI,
|
|
706
707
|
registerPipelineForDebug: () => registerPipelineForDebug
|
|
@@ -708,6 +709,9 @@ __export(PipelineDebugger_exports, {
|
|
|
708
709
|
function registerPipelineForDebug(pipeline) {
|
|
709
710
|
_activePipeline = pipeline;
|
|
710
711
|
}
|
|
712
|
+
function clearRunHistory() {
|
|
713
|
+
runHistory.length = 0;
|
|
714
|
+
}
|
|
711
715
|
function getOrigin(card) {
|
|
712
716
|
const firstEntry = card.provenance[0];
|
|
713
717
|
if (!firstEntry) return "unknown";
|
|
@@ -736,7 +740,7 @@ function parseCardElo(provenance) {
|
|
|
736
740
|
}
|
|
737
741
|
function buildRunReport(courseId, courseName, generatorName, generators, generatedCount, filters, allCards, selectedCards, userElo, hints) {
|
|
738
742
|
const selectedIds = new Set(selectedCards.map((c) => c.cardId));
|
|
739
|
-
const
|
|
743
|
+
const toReport = (card) => ({
|
|
740
744
|
cardId: card.cardId,
|
|
741
745
|
courseId: card.courseId,
|
|
742
746
|
origin: getOrigin(card),
|
|
@@ -746,7 +750,47 @@ function buildRunReport(courseId, courseName, generatorName, generators, generat
|
|
|
746
750
|
provenance: card.provenance,
|
|
747
751
|
tags: card.tags,
|
|
748
752
|
selected: selectedIds.has(card.cardId)
|
|
749
|
-
})
|
|
753
|
+
});
|
|
754
|
+
const selectedReported = [];
|
|
755
|
+
const nearMissReported = [];
|
|
756
|
+
const discardedTailCards = [];
|
|
757
|
+
let nonSelectedSeen = 0;
|
|
758
|
+
for (const card of allCards) {
|
|
759
|
+
if (selectedIds.has(card.cardId)) {
|
|
760
|
+
selectedReported.push(toReport(card));
|
|
761
|
+
} else if (nonSelectedSeen < DISCARDED_KEEP_TOP) {
|
|
762
|
+
nearMissReported.push(toReport(card));
|
|
763
|
+
nonSelectedSeen++;
|
|
764
|
+
} else {
|
|
765
|
+
discardedTailCards.push(card);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
const cards = [...selectedReported, ...nearMissReported];
|
|
769
|
+
let discardedTail;
|
|
770
|
+
if (discardedTailCards.length > 0) {
|
|
771
|
+
let scoreMin = Infinity;
|
|
772
|
+
let scoreMax = -Infinity;
|
|
773
|
+
let eloMin = Infinity;
|
|
774
|
+
let eloMax = -Infinity;
|
|
775
|
+
let eloSeen = false;
|
|
776
|
+
for (const c of discardedTailCards) {
|
|
777
|
+
if (c.score < scoreMin) scoreMin = c.score;
|
|
778
|
+
if (c.score > scoreMax) scoreMax = c.score;
|
|
779
|
+
const elo = parseCardElo(c.provenance);
|
|
780
|
+
if (elo !== void 0) {
|
|
781
|
+
eloSeen = true;
|
|
782
|
+
if (elo < eloMin) eloMin = elo;
|
|
783
|
+
if (elo > eloMax) eloMax = elo;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
const eloFragment = eloSeen ? `, ELO ${eloMin}\u2013${eloMax}` : "";
|
|
787
|
+
discardedTail = {
|
|
788
|
+
count: discardedTailCards.length,
|
|
789
|
+
scoreRange: [scoreMin, scoreMax],
|
|
790
|
+
eloRange: eloSeen ? [eloMin, eloMax] : void 0,
|
|
791
|
+
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.`
|
|
792
|
+
};
|
|
793
|
+
}
|
|
750
794
|
const reviewsSelected = selectedCards.filter((c) => getOrigin(c) === "review").length;
|
|
751
795
|
const newSelected = selectedCards.filter((c) => getOrigin(c) === "new").length;
|
|
752
796
|
return {
|
|
@@ -761,7 +805,8 @@ function buildRunReport(courseId, courseName, generatorName, generators, generat
|
|
|
761
805
|
finalCount: selectedCards.length,
|
|
762
806
|
reviewsSelected,
|
|
763
807
|
newSelected,
|
|
764
|
-
cards
|
|
808
|
+
cards,
|
|
809
|
+
discardedTail
|
|
765
810
|
};
|
|
766
811
|
}
|
|
767
812
|
function formatProvenance(provenance) {
|
|
@@ -797,6 +842,44 @@ function printRunSummary(run) {
|
|
|
797
842
|
);
|
|
798
843
|
console.groupEnd();
|
|
799
844
|
}
|
|
845
|
+
function escapeHtml(s) {
|
|
846
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
847
|
+
}
|
|
848
|
+
function escapeAttr(s) {
|
|
849
|
+
return escapeHtml(s).replace(/"/g, """);
|
|
850
|
+
}
|
|
851
|
+
function copyTextToClipboard(text, btn) {
|
|
852
|
+
const done = () => {
|
|
853
|
+
if (!btn) return;
|
|
854
|
+
const orig = btn.textContent ?? "Copy";
|
|
855
|
+
btn.textContent = "Copied!";
|
|
856
|
+
btn.classList.add("copied");
|
|
857
|
+
setTimeout(() => {
|
|
858
|
+
btn.textContent = orig;
|
|
859
|
+
btn.classList.remove("copied");
|
|
860
|
+
}, 1200);
|
|
861
|
+
};
|
|
862
|
+
const fallback = () => {
|
|
863
|
+
const ta = document.createElement("textarea");
|
|
864
|
+
ta.value = text;
|
|
865
|
+
ta.style.position = "fixed";
|
|
866
|
+
ta.style.opacity = "0";
|
|
867
|
+
document.body.appendChild(ta);
|
|
868
|
+
ta.select();
|
|
869
|
+
try {
|
|
870
|
+
document.execCommand("copy");
|
|
871
|
+
} catch (e) {
|
|
872
|
+
logger.warn(`[Pipeline Debug] Copy failed: ${e}`);
|
|
873
|
+
}
|
|
874
|
+
document.body.removeChild(ta);
|
|
875
|
+
done();
|
|
876
|
+
};
|
|
877
|
+
if (navigator.clipboard?.writeText) {
|
|
878
|
+
navigator.clipboard.writeText(text).then(done).catch(fallback);
|
|
879
|
+
} else {
|
|
880
|
+
fallback();
|
|
881
|
+
}
|
|
882
|
+
}
|
|
800
883
|
function renderUI() {
|
|
801
884
|
if (!_uiContainer) return;
|
|
802
885
|
const runs = runHistory;
|
|
@@ -855,6 +938,13 @@ function renderUI() {
|
|
|
855
938
|
#sk-pipeline-debugger .close-btn { background: #dc3545; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; }
|
|
856
939
|
#sk-pipeline-debugger .search-box { margin-bottom: 1rem; width: 100%; padding: 0.5rem; border: 1px solid #ced4da; border-radius: 4px; }
|
|
857
940
|
#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; }
|
|
941
|
+
#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; }
|
|
942
|
+
#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; }
|
|
943
|
+
#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; }
|
|
944
|
+
#sk-pipeline-debugger .copy-btn:hover { background: #0b5ed7; }
|
|
945
|
+
#sk-pipeline-debugger .copy-btn.copied { background: #198754; }
|
|
946
|
+
#sk-pipeline-debugger .section-head { display: flex; align-items: center; justify-content: space-between; margin-top: 1rem; }
|
|
947
|
+
#sk-pipeline-debugger .section-head h3 { margin: 0; }
|
|
858
948
|
`;
|
|
859
949
|
const runListHtml = runs.length === 0 ? '<div style="padding: 1rem;">No runs captured yet.</div>' : runs.map(
|
|
860
950
|
(r, i) => `
|
|
@@ -862,6 +952,7 @@ function renderUI() {
|
|
|
862
952
|
<strong>${r.timestamp.toLocaleTimeString()}</strong><br/>
|
|
863
953
|
<small>${r.courseName || r.courseId.slice(0, 8)}</small><br/>
|
|
864
954
|
<small>${r.finalCount} cards selected</small>
|
|
955
|
+
${r.hints?._label ? `<br/><span class="run-label" title="${escapeAttr(r.hints._label)}">${escapeHtml(r.hints._label)}</span>` : ""}
|
|
865
956
|
</div>
|
|
866
957
|
`
|
|
867
958
|
).join("");
|
|
@@ -870,11 +961,13 @@ function renderUI() {
|
|
|
870
961
|
const filteredCards = selectedRun.cards.filter(
|
|
871
962
|
(c) => !_cardSearchQuery || c.cardId.toLowerCase().includes(_cardSearchQuery.toLowerCase())
|
|
872
963
|
);
|
|
964
|
+
const labelText = selectedRun.hints?._label ?? "(no label)";
|
|
873
965
|
detailsHtml = `
|
|
874
966
|
<h2>Run: ${selectedRun.runId}</h2>
|
|
967
|
+
<div class="label-banner" title="${escapeAttr(labelText)}">${escapeHtml(labelText)}</div>
|
|
875
968
|
<p>
|
|
876
|
-
<strong>Time:</strong> ${selectedRun.timestamp.toLocaleString()} |
|
|
877
|
-
<strong>Course:</strong> ${selectedRun.courseName || selectedRun.courseId} |
|
|
969
|
+
<strong>Time:</strong> ${selectedRun.timestamp.toLocaleString()} |
|
|
970
|
+
<strong>Course:</strong> ${selectedRun.courseName || selectedRun.courseId} |
|
|
878
971
|
<strong>User ELO:</strong> ${selectedRun.userElo ?? "unknown"}
|
|
879
972
|
</p>
|
|
880
973
|
|
|
@@ -889,7 +982,10 @@ function renderUI() {
|
|
|
889
982
|
</table>
|
|
890
983
|
|
|
891
984
|
${selectedRun.hints ? `
|
|
892
|
-
<
|
|
985
|
+
<div class="section-head">
|
|
986
|
+
<h3>Ephemeral Hints</h3>
|
|
987
|
+
<button class="copy-btn" onclick="window.skuilder.pipeline._copyConfig('${selectedRun.runId}', this)">Copy config</button>
|
|
988
|
+
</div>
|
|
893
989
|
<table>
|
|
894
990
|
${selectedRun.hints._label ? `<tr><th>Label</th><td>${selectedRun.hints._label}</td></tr>` : ""}
|
|
895
991
|
${selectedRun.hints.boostTags ? `<tr><th>Boost Tags</th><td><pre style="margin:0">${JSON.stringify(selectedRun.hints.boostTags, null, 2)}</pre></td></tr>` : ""}
|
|
@@ -913,7 +1009,10 @@ function renderUI() {
|
|
|
913
1009
|
</tbody>
|
|
914
1010
|
</table>
|
|
915
1011
|
|
|
916
|
-
<
|
|
1012
|
+
<div class="section-head">
|
|
1013
|
+
<h3>Cards (${selectedRun.finalCount} selected / ${selectedRun.cards.length} total)</h3>
|
|
1014
|
+
<button class="copy-btn" onclick="window.skuilder.pipeline._copyResults('${selectedRun.runId}', this)">Copy results</button>
|
|
1015
|
+
</div>
|
|
917
1016
|
<input type="text" class="search-box" placeholder="Search Card ID..." value="${_cardSearchQuery}" oninput="window.skuilder.pipeline._setSearch(this.value)">
|
|
918
1017
|
|
|
919
1018
|
<table>
|
|
@@ -959,7 +1058,7 @@ function mountPipelineDebugger() {
|
|
|
959
1058
|
win.skuilder = win.skuilder || {};
|
|
960
1059
|
win.skuilder.pipeline = pipelineDebugAPI;
|
|
961
1060
|
}
|
|
962
|
-
var _activePipeline, MAX_RUNS, runHistory, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI;
|
|
1061
|
+
var _activePipeline, MAX_RUNS, runHistory, DISCARDED_KEEP_TOP, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI;
|
|
963
1062
|
var init_PipelineDebugger = __esm({
|
|
964
1063
|
"src/core/navigators/PipelineDebugger.ts"() {
|
|
965
1064
|
"use strict";
|
|
@@ -968,6 +1067,7 @@ var init_PipelineDebugger = __esm({
|
|
|
968
1067
|
_activePipeline = null;
|
|
969
1068
|
MAX_RUNS = 10;
|
|
970
1069
|
runHistory = [];
|
|
1070
|
+
DISCARDED_KEEP_TOP = 25;
|
|
971
1071
|
_uiContainer = null;
|
|
972
1072
|
_selectedRunIndex = null;
|
|
973
1073
|
_cardSearchQuery = "";
|
|
@@ -1032,7 +1132,14 @@ var init_PipelineDebugger = __esm({
|
|
|
1032
1132
|
return;
|
|
1033
1133
|
}
|
|
1034
1134
|
}
|
|
1035
|
-
|
|
1135
|
+
const runsWithTails = runHistory.filter((r) => r.discardedTail && r.discardedTail.count > 0);
|
|
1136
|
+
if (runsWithTails.length > 0) {
|
|
1137
|
+
logger.info(
|
|
1138
|
+
`[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.`
|
|
1139
|
+
);
|
|
1140
|
+
} else {
|
|
1141
|
+
logger.info(`[Pipeline Debug] Card '${cardId}' not found in recent runs.`);
|
|
1142
|
+
}
|
|
1036
1143
|
},
|
|
1037
1144
|
/**
|
|
1038
1145
|
* Explain why reviews may or may not have been selected.
|
|
@@ -1337,6 +1444,50 @@ var init_PipelineDebugger = __esm({
|
|
|
1337
1444
|
_cardSearchQuery = query;
|
|
1338
1445
|
renderUI();
|
|
1339
1446
|
},
|
|
1447
|
+
/**
|
|
1448
|
+
* Internal UI helpers
|
|
1449
|
+
* @internal
|
|
1450
|
+
*/
|
|
1451
|
+
_copyConfig(runId, btn) {
|
|
1452
|
+
const run = runHistory.find((r) => r.runId === runId);
|
|
1453
|
+
if (!run) return;
|
|
1454
|
+
const payload = {
|
|
1455
|
+
runId: run.runId,
|
|
1456
|
+
timestamp: run.timestamp.toISOString(),
|
|
1457
|
+
courseId: run.courseId,
|
|
1458
|
+
courseName: run.courseName,
|
|
1459
|
+
hints: run.hints ?? null
|
|
1460
|
+
};
|
|
1461
|
+
copyTextToClipboard(JSON.stringify(payload, null, 2), btn);
|
|
1462
|
+
},
|
|
1463
|
+
/**
|
|
1464
|
+
* Internal UI helpers
|
|
1465
|
+
* @internal
|
|
1466
|
+
*
|
|
1467
|
+
* Copies an "abridged" view of results: just the selected cards with their
|
|
1468
|
+
* generator, origin, final score, and the top provenance reason. Designed
|
|
1469
|
+
* for pasting into bug reports without flooding with full provenance.
|
|
1470
|
+
*/
|
|
1471
|
+
_copyResults(runId, btn) {
|
|
1472
|
+
const run = runHistory.find((r) => r.runId === runId);
|
|
1473
|
+
if (!run) return;
|
|
1474
|
+
const selected = run.cards.filter((c) => c.selected).sort((a, b) => b.finalScore - a.finalScore).map((c) => ({
|
|
1475
|
+
cardId: c.cardId,
|
|
1476
|
+
generator: c.generator,
|
|
1477
|
+
origin: c.origin,
|
|
1478
|
+
score: Number(c.finalScore.toFixed(3)),
|
|
1479
|
+
topReason: c.provenance[0]?.reason ?? ""
|
|
1480
|
+
}));
|
|
1481
|
+
const payload = {
|
|
1482
|
+
runId: run.runId,
|
|
1483
|
+
label: run.hints?._label ?? null,
|
|
1484
|
+
finalCount: run.finalCount,
|
|
1485
|
+
newSelected: run.newSelected,
|
|
1486
|
+
reviewsSelected: run.reviewsSelected,
|
|
1487
|
+
selected
|
|
1488
|
+
};
|
|
1489
|
+
copyTextToClipboard(JSON.stringify(payload, null, 2), btn);
|
|
1490
|
+
},
|
|
1340
1491
|
/**
|
|
1341
1492
|
* Show help.
|
|
1342
1493
|
*/
|
|
@@ -5606,12 +5757,12 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5606
5757
|
async getWeightedCards(limit) {
|
|
5607
5758
|
const u = await this._getCurrentUser();
|
|
5608
5759
|
try {
|
|
5609
|
-
const
|
|
5760
|
+
const navigator2 = await this.createNavigator(u);
|
|
5610
5761
|
if (this._pendingHints) {
|
|
5611
|
-
|
|
5762
|
+
navigator2.setEphemeralHints(this._pendingHints);
|
|
5612
5763
|
this._pendingHints = null;
|
|
5613
5764
|
}
|
|
5614
|
-
return
|
|
5765
|
+
return navigator2.getWeightedCards(limit);
|
|
5615
5766
|
} catch (e) {
|
|
5616
5767
|
logger.error(`[courseDB] Error getting weighted cards: ${e}`);
|
|
5617
5768
|
throw e;
|