@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
package/dist/core/index.d.cts
CHANGED
|
@@ -364,6 +364,19 @@ interface PipelineRunReport {
|
|
|
364
364
|
finalCount: number;
|
|
365
365
|
reviewsSelected: number;
|
|
366
366
|
newSelected: number;
|
|
367
|
+
/**
|
|
368
|
+
* Card data for inspection.
|
|
369
|
+
*
|
|
370
|
+
* To keep the in-memory ring buffer small, this contains:
|
|
371
|
+
* - All selected cards (the actual session output).
|
|
372
|
+
* - The top-N highest-scoring non-selected cards (see DISCARDED_KEEP_TOP),
|
|
373
|
+
* useful for understanding "near misses" and filter behavior.
|
|
374
|
+
*
|
|
375
|
+
* The remaining low-score tail of the candidate pool (mostly ELO-window
|
|
376
|
+
* pull remnants that filters scored down) is summarized in `discardedTail`
|
|
377
|
+
* rather than retained verbatim — each retained card carries a multi-KB
|
|
378
|
+
* provenance trail, and the tail is typically hundreds of cards per run.
|
|
379
|
+
*/
|
|
367
380
|
cards: Array<{
|
|
368
381
|
cardId: string;
|
|
369
382
|
courseId: string;
|
|
@@ -376,6 +389,25 @@ interface PipelineRunReport {
|
|
|
376
389
|
tags?: string[];
|
|
377
390
|
selected: boolean;
|
|
378
391
|
}>;
|
|
392
|
+
/**
|
|
393
|
+
* Summary of the discarded tail of the candidate pool — cards that were
|
|
394
|
+
* generated and scored but neither selected nor retained in `cards`.
|
|
395
|
+
*
|
|
396
|
+
* Provides breadcrumbs for "where did card X go?" investigations without
|
|
397
|
+
* the memory cost of keeping every dropped candidate's provenance.
|
|
398
|
+
*
|
|
399
|
+
* Future: may carry a bloom filter of discarded cardIds so callers can
|
|
400
|
+
* ask "was X in this run's discard tail?" with bounded memory cost.
|
|
401
|
+
*/
|
|
402
|
+
discardedTail?: {
|
|
403
|
+
count: number;
|
|
404
|
+
/** [min, max] finalScore across the discarded tail. */
|
|
405
|
+
scoreRange?: [number, number];
|
|
406
|
+
/** [min, max] cardElo across the discarded tail (where parseable). */
|
|
407
|
+
eloRange?: [number, number];
|
|
408
|
+
/** Human-readable note for console display. */
|
|
409
|
+
note: string;
|
|
410
|
+
};
|
|
379
411
|
}
|
|
380
412
|
/**
|
|
381
413
|
* Console API object exposed on window.skuilder.pipeline
|
|
@@ -468,6 +500,20 @@ declare const pipelineDebugAPI: {
|
|
|
468
500
|
* @internal
|
|
469
501
|
*/
|
|
470
502
|
_setSearch(query: string): void;
|
|
503
|
+
/**
|
|
504
|
+
* Internal UI helpers
|
|
505
|
+
* @internal
|
|
506
|
+
*/
|
|
507
|
+
_copyConfig(runId: string, btn?: HTMLElement): void;
|
|
508
|
+
/**
|
|
509
|
+
* Internal UI helpers
|
|
510
|
+
* @internal
|
|
511
|
+
*
|
|
512
|
+
* Copies an "abridged" view of results: just the selected cards with their
|
|
513
|
+
* generator, origin, final score, and the top provenance reason. Designed
|
|
514
|
+
* for pasting into bug reports without flooding with full provenance.
|
|
515
|
+
*/
|
|
516
|
+
_copyResults(runId: string, btn?: HTMLElement): void;
|
|
471
517
|
/**
|
|
472
518
|
* Show help.
|
|
473
519
|
*/
|
package/dist/core/index.d.ts
CHANGED
|
@@ -364,6 +364,19 @@ interface PipelineRunReport {
|
|
|
364
364
|
finalCount: number;
|
|
365
365
|
reviewsSelected: number;
|
|
366
366
|
newSelected: number;
|
|
367
|
+
/**
|
|
368
|
+
* Card data for inspection.
|
|
369
|
+
*
|
|
370
|
+
* To keep the in-memory ring buffer small, this contains:
|
|
371
|
+
* - All selected cards (the actual session output).
|
|
372
|
+
* - The top-N highest-scoring non-selected cards (see DISCARDED_KEEP_TOP),
|
|
373
|
+
* useful for understanding "near misses" and filter behavior.
|
|
374
|
+
*
|
|
375
|
+
* The remaining low-score tail of the candidate pool (mostly ELO-window
|
|
376
|
+
* pull remnants that filters scored down) is summarized in `discardedTail`
|
|
377
|
+
* rather than retained verbatim — each retained card carries a multi-KB
|
|
378
|
+
* provenance trail, and the tail is typically hundreds of cards per run.
|
|
379
|
+
*/
|
|
367
380
|
cards: Array<{
|
|
368
381
|
cardId: string;
|
|
369
382
|
courseId: string;
|
|
@@ -376,6 +389,25 @@ interface PipelineRunReport {
|
|
|
376
389
|
tags?: string[];
|
|
377
390
|
selected: boolean;
|
|
378
391
|
}>;
|
|
392
|
+
/**
|
|
393
|
+
* Summary of the discarded tail of the candidate pool — cards that were
|
|
394
|
+
* generated and scored but neither selected nor retained in `cards`.
|
|
395
|
+
*
|
|
396
|
+
* Provides breadcrumbs for "where did card X go?" investigations without
|
|
397
|
+
* the memory cost of keeping every dropped candidate's provenance.
|
|
398
|
+
*
|
|
399
|
+
* Future: may carry a bloom filter of discarded cardIds so callers can
|
|
400
|
+
* ask "was X in this run's discard tail?" with bounded memory cost.
|
|
401
|
+
*/
|
|
402
|
+
discardedTail?: {
|
|
403
|
+
count: number;
|
|
404
|
+
/** [min, max] finalScore across the discarded tail. */
|
|
405
|
+
scoreRange?: [number, number];
|
|
406
|
+
/** [min, max] cardElo across the discarded tail (where parseable). */
|
|
407
|
+
eloRange?: [number, number];
|
|
408
|
+
/** Human-readable note for console display. */
|
|
409
|
+
note: string;
|
|
410
|
+
};
|
|
379
411
|
}
|
|
380
412
|
/**
|
|
381
413
|
* Console API object exposed on window.skuilder.pipeline
|
|
@@ -468,6 +500,20 @@ declare const pipelineDebugAPI: {
|
|
|
468
500
|
* @internal
|
|
469
501
|
*/
|
|
470
502
|
_setSearch(query: string): void;
|
|
503
|
+
/**
|
|
504
|
+
* Internal UI helpers
|
|
505
|
+
* @internal
|
|
506
|
+
*/
|
|
507
|
+
_copyConfig(runId: string, btn?: HTMLElement): void;
|
|
508
|
+
/**
|
|
509
|
+
* Internal UI helpers
|
|
510
|
+
* @internal
|
|
511
|
+
*
|
|
512
|
+
* Copies an "abridged" view of results: just the selected cards with their
|
|
513
|
+
* generator, origin, final score, and the top provenance reason. Designed
|
|
514
|
+
* for pasting into bug reports without flooding with full provenance.
|
|
515
|
+
*/
|
|
516
|
+
_copyResults(runId: string, btn?: HTMLElement): void;
|
|
471
517
|
/**
|
|
472
518
|
* Show help.
|
|
473
519
|
*/
|
package/dist/core/index.js
CHANGED
|
@@ -724,6 +724,7 @@ var PipelineDebugger_exports = {};
|
|
|
724
724
|
__export(PipelineDebugger_exports, {
|
|
725
725
|
buildRunReport: () => buildRunReport,
|
|
726
726
|
captureRun: () => captureRun,
|
|
727
|
+
clearRunHistory: () => clearRunHistory,
|
|
727
728
|
mountPipelineDebugger: () => mountPipelineDebugger,
|
|
728
729
|
pipelineDebugAPI: () => pipelineDebugAPI,
|
|
729
730
|
registerPipelineForDebug: () => registerPipelineForDebug
|
|
@@ -731,6 +732,9 @@ __export(PipelineDebugger_exports, {
|
|
|
731
732
|
function registerPipelineForDebug(pipeline) {
|
|
732
733
|
_activePipeline = pipeline;
|
|
733
734
|
}
|
|
735
|
+
function clearRunHistory() {
|
|
736
|
+
runHistory.length = 0;
|
|
737
|
+
}
|
|
734
738
|
function getOrigin(card) {
|
|
735
739
|
const firstEntry = card.provenance[0];
|
|
736
740
|
if (!firstEntry) return "unknown";
|
|
@@ -759,7 +763,7 @@ function parseCardElo(provenance) {
|
|
|
759
763
|
}
|
|
760
764
|
function buildRunReport(courseId, courseName, generatorName, generators, generatedCount, filters, allCards, selectedCards, userElo, hints) {
|
|
761
765
|
const selectedIds = new Set(selectedCards.map((c) => c.cardId));
|
|
762
|
-
const
|
|
766
|
+
const toReport = (card) => ({
|
|
763
767
|
cardId: card.cardId,
|
|
764
768
|
courseId: card.courseId,
|
|
765
769
|
origin: getOrigin(card),
|
|
@@ -769,7 +773,47 @@ function buildRunReport(courseId, courseName, generatorName, generators, generat
|
|
|
769
773
|
provenance: card.provenance,
|
|
770
774
|
tags: card.tags,
|
|
771
775
|
selected: selectedIds.has(card.cardId)
|
|
772
|
-
})
|
|
776
|
+
});
|
|
777
|
+
const selectedReported = [];
|
|
778
|
+
const nearMissReported = [];
|
|
779
|
+
const discardedTailCards = [];
|
|
780
|
+
let nonSelectedSeen = 0;
|
|
781
|
+
for (const card of allCards) {
|
|
782
|
+
if (selectedIds.has(card.cardId)) {
|
|
783
|
+
selectedReported.push(toReport(card));
|
|
784
|
+
} else if (nonSelectedSeen < DISCARDED_KEEP_TOP) {
|
|
785
|
+
nearMissReported.push(toReport(card));
|
|
786
|
+
nonSelectedSeen++;
|
|
787
|
+
} else {
|
|
788
|
+
discardedTailCards.push(card);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const cards = [...selectedReported, ...nearMissReported];
|
|
792
|
+
let discardedTail;
|
|
793
|
+
if (discardedTailCards.length > 0) {
|
|
794
|
+
let scoreMin = Infinity;
|
|
795
|
+
let scoreMax = -Infinity;
|
|
796
|
+
let eloMin = Infinity;
|
|
797
|
+
let eloMax = -Infinity;
|
|
798
|
+
let eloSeen = false;
|
|
799
|
+
for (const c of discardedTailCards) {
|
|
800
|
+
if (c.score < scoreMin) scoreMin = c.score;
|
|
801
|
+
if (c.score > scoreMax) scoreMax = c.score;
|
|
802
|
+
const elo = parseCardElo(c.provenance);
|
|
803
|
+
if (elo !== void 0) {
|
|
804
|
+
eloSeen = true;
|
|
805
|
+
if (elo < eloMin) eloMin = elo;
|
|
806
|
+
if (elo > eloMax) eloMax = elo;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const eloFragment = eloSeen ? `, ELO ${eloMin}\u2013${eloMax}` : "";
|
|
810
|
+
discardedTail = {
|
|
811
|
+
count: discardedTailCards.length,
|
|
812
|
+
scoreRange: [scoreMin, scoreMax],
|
|
813
|
+
eloRange: eloSeen ? [eloMin, eloMax] : void 0,
|
|
814
|
+
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.`
|
|
815
|
+
};
|
|
816
|
+
}
|
|
773
817
|
const reviewsSelected = selectedCards.filter((c) => getOrigin(c) === "review").length;
|
|
774
818
|
const newSelected = selectedCards.filter((c) => getOrigin(c) === "new").length;
|
|
775
819
|
return {
|
|
@@ -784,7 +828,8 @@ function buildRunReport(courseId, courseName, generatorName, generators, generat
|
|
|
784
828
|
finalCount: selectedCards.length,
|
|
785
829
|
reviewsSelected,
|
|
786
830
|
newSelected,
|
|
787
|
-
cards
|
|
831
|
+
cards,
|
|
832
|
+
discardedTail
|
|
788
833
|
};
|
|
789
834
|
}
|
|
790
835
|
function formatProvenance(provenance) {
|
|
@@ -820,6 +865,44 @@ function printRunSummary(run) {
|
|
|
820
865
|
);
|
|
821
866
|
console.groupEnd();
|
|
822
867
|
}
|
|
868
|
+
function escapeHtml(s) {
|
|
869
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
870
|
+
}
|
|
871
|
+
function escapeAttr(s) {
|
|
872
|
+
return escapeHtml(s).replace(/"/g, """);
|
|
873
|
+
}
|
|
874
|
+
function copyTextToClipboard(text, btn) {
|
|
875
|
+
const done = () => {
|
|
876
|
+
if (!btn) return;
|
|
877
|
+
const orig = btn.textContent ?? "Copy";
|
|
878
|
+
btn.textContent = "Copied!";
|
|
879
|
+
btn.classList.add("copied");
|
|
880
|
+
setTimeout(() => {
|
|
881
|
+
btn.textContent = orig;
|
|
882
|
+
btn.classList.remove("copied");
|
|
883
|
+
}, 1200);
|
|
884
|
+
};
|
|
885
|
+
const fallback = () => {
|
|
886
|
+
const ta = document.createElement("textarea");
|
|
887
|
+
ta.value = text;
|
|
888
|
+
ta.style.position = "fixed";
|
|
889
|
+
ta.style.opacity = "0";
|
|
890
|
+
document.body.appendChild(ta);
|
|
891
|
+
ta.select();
|
|
892
|
+
try {
|
|
893
|
+
document.execCommand("copy");
|
|
894
|
+
} catch (e) {
|
|
895
|
+
logger.warn(`[Pipeline Debug] Copy failed: ${e}`);
|
|
896
|
+
}
|
|
897
|
+
document.body.removeChild(ta);
|
|
898
|
+
done();
|
|
899
|
+
};
|
|
900
|
+
if (navigator.clipboard?.writeText) {
|
|
901
|
+
navigator.clipboard.writeText(text).then(done).catch(fallback);
|
|
902
|
+
} else {
|
|
903
|
+
fallback();
|
|
904
|
+
}
|
|
905
|
+
}
|
|
823
906
|
function renderUI() {
|
|
824
907
|
if (!_uiContainer) return;
|
|
825
908
|
const runs = runHistory;
|
|
@@ -878,6 +961,13 @@ function renderUI() {
|
|
|
878
961
|
#sk-pipeline-debugger .close-btn { background: #dc3545; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; }
|
|
879
962
|
#sk-pipeline-debugger .search-box { margin-bottom: 1rem; width: 100%; padding: 0.5rem; border: 1px solid #ced4da; border-radius: 4px; }
|
|
880
963
|
#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; }
|
|
964
|
+
#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; }
|
|
965
|
+
#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; }
|
|
966
|
+
#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; }
|
|
967
|
+
#sk-pipeline-debugger .copy-btn:hover { background: #0b5ed7; }
|
|
968
|
+
#sk-pipeline-debugger .copy-btn.copied { background: #198754; }
|
|
969
|
+
#sk-pipeline-debugger .section-head { display: flex; align-items: center; justify-content: space-between; margin-top: 1rem; }
|
|
970
|
+
#sk-pipeline-debugger .section-head h3 { margin: 0; }
|
|
881
971
|
`;
|
|
882
972
|
const runListHtml = runs.length === 0 ? '<div style="padding: 1rem;">No runs captured yet.</div>' : runs.map(
|
|
883
973
|
(r, i) => `
|
|
@@ -885,6 +975,7 @@ function renderUI() {
|
|
|
885
975
|
<strong>${r.timestamp.toLocaleTimeString()}</strong><br/>
|
|
886
976
|
<small>${r.courseName || r.courseId.slice(0, 8)}</small><br/>
|
|
887
977
|
<small>${r.finalCount} cards selected</small>
|
|
978
|
+
${r.hints?._label ? `<br/><span class="run-label" title="${escapeAttr(r.hints._label)}">${escapeHtml(r.hints._label)}</span>` : ""}
|
|
888
979
|
</div>
|
|
889
980
|
`
|
|
890
981
|
).join("");
|
|
@@ -893,11 +984,13 @@ function renderUI() {
|
|
|
893
984
|
const filteredCards = selectedRun.cards.filter(
|
|
894
985
|
(c) => !_cardSearchQuery || c.cardId.toLowerCase().includes(_cardSearchQuery.toLowerCase())
|
|
895
986
|
);
|
|
987
|
+
const labelText = selectedRun.hints?._label ?? "(no label)";
|
|
896
988
|
detailsHtml = `
|
|
897
989
|
<h2>Run: ${selectedRun.runId}</h2>
|
|
990
|
+
<div class="label-banner" title="${escapeAttr(labelText)}">${escapeHtml(labelText)}</div>
|
|
898
991
|
<p>
|
|
899
|
-
<strong>Time:</strong> ${selectedRun.timestamp.toLocaleString()} |
|
|
900
|
-
<strong>Course:</strong> ${selectedRun.courseName || selectedRun.courseId} |
|
|
992
|
+
<strong>Time:</strong> ${selectedRun.timestamp.toLocaleString()} |
|
|
993
|
+
<strong>Course:</strong> ${selectedRun.courseName || selectedRun.courseId} |
|
|
901
994
|
<strong>User ELO:</strong> ${selectedRun.userElo ?? "unknown"}
|
|
902
995
|
</p>
|
|
903
996
|
|
|
@@ -912,7 +1005,10 @@ function renderUI() {
|
|
|
912
1005
|
</table>
|
|
913
1006
|
|
|
914
1007
|
${selectedRun.hints ? `
|
|
915
|
-
<
|
|
1008
|
+
<div class="section-head">
|
|
1009
|
+
<h3>Ephemeral Hints</h3>
|
|
1010
|
+
<button class="copy-btn" onclick="window.skuilder.pipeline._copyConfig('${selectedRun.runId}', this)">Copy config</button>
|
|
1011
|
+
</div>
|
|
916
1012
|
<table>
|
|
917
1013
|
${selectedRun.hints._label ? `<tr><th>Label</th><td>${selectedRun.hints._label}</td></tr>` : ""}
|
|
918
1014
|
${selectedRun.hints.boostTags ? `<tr><th>Boost Tags</th><td><pre style="margin:0">${JSON.stringify(selectedRun.hints.boostTags, null, 2)}</pre></td></tr>` : ""}
|
|
@@ -936,7 +1032,10 @@ function renderUI() {
|
|
|
936
1032
|
</tbody>
|
|
937
1033
|
</table>
|
|
938
1034
|
|
|
939
|
-
<
|
|
1035
|
+
<div class="section-head">
|
|
1036
|
+
<h3>Cards (${selectedRun.finalCount} selected / ${selectedRun.cards.length} total)</h3>
|
|
1037
|
+
<button class="copy-btn" onclick="window.skuilder.pipeline._copyResults('${selectedRun.runId}', this)">Copy results</button>
|
|
1038
|
+
</div>
|
|
940
1039
|
<input type="text" class="search-box" placeholder="Search Card ID..." value="${_cardSearchQuery}" oninput="window.skuilder.pipeline._setSearch(this.value)">
|
|
941
1040
|
|
|
942
1041
|
<table>
|
|
@@ -982,7 +1081,7 @@ function mountPipelineDebugger() {
|
|
|
982
1081
|
win.skuilder = win.skuilder || {};
|
|
983
1082
|
win.skuilder.pipeline = pipelineDebugAPI;
|
|
984
1083
|
}
|
|
985
|
-
var _activePipeline, MAX_RUNS, runHistory, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI;
|
|
1084
|
+
var _activePipeline, MAX_RUNS, runHistory, DISCARDED_KEEP_TOP, _uiContainer, _selectedRunIndex, _cardSearchQuery, pipelineDebugAPI;
|
|
986
1085
|
var init_PipelineDebugger = __esm({
|
|
987
1086
|
"src/core/navigators/PipelineDebugger.ts"() {
|
|
988
1087
|
"use strict";
|
|
@@ -991,6 +1090,7 @@ var init_PipelineDebugger = __esm({
|
|
|
991
1090
|
_activePipeline = null;
|
|
992
1091
|
MAX_RUNS = 10;
|
|
993
1092
|
runHistory = [];
|
|
1093
|
+
DISCARDED_KEEP_TOP = 25;
|
|
994
1094
|
_uiContainer = null;
|
|
995
1095
|
_selectedRunIndex = null;
|
|
996
1096
|
_cardSearchQuery = "";
|
|
@@ -1055,7 +1155,14 @@ var init_PipelineDebugger = __esm({
|
|
|
1055
1155
|
return;
|
|
1056
1156
|
}
|
|
1057
1157
|
}
|
|
1058
|
-
|
|
1158
|
+
const runsWithTails = runHistory.filter((r) => r.discardedTail && r.discardedTail.count > 0);
|
|
1159
|
+
if (runsWithTails.length > 0) {
|
|
1160
|
+
logger.info(
|
|
1161
|
+
`[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.`
|
|
1162
|
+
);
|
|
1163
|
+
} else {
|
|
1164
|
+
logger.info(`[Pipeline Debug] Card '${cardId}' not found in recent runs.`);
|
|
1165
|
+
}
|
|
1059
1166
|
},
|
|
1060
1167
|
/**
|
|
1061
1168
|
* Explain why reviews may or may not have been selected.
|
|
@@ -1360,6 +1467,50 @@ var init_PipelineDebugger = __esm({
|
|
|
1360
1467
|
_cardSearchQuery = query;
|
|
1361
1468
|
renderUI();
|
|
1362
1469
|
},
|
|
1470
|
+
/**
|
|
1471
|
+
* Internal UI helpers
|
|
1472
|
+
* @internal
|
|
1473
|
+
*/
|
|
1474
|
+
_copyConfig(runId, btn) {
|
|
1475
|
+
const run = runHistory.find((r) => r.runId === runId);
|
|
1476
|
+
if (!run) return;
|
|
1477
|
+
const payload = {
|
|
1478
|
+
runId: run.runId,
|
|
1479
|
+
timestamp: run.timestamp.toISOString(),
|
|
1480
|
+
courseId: run.courseId,
|
|
1481
|
+
courseName: run.courseName,
|
|
1482
|
+
hints: run.hints ?? null
|
|
1483
|
+
};
|
|
1484
|
+
copyTextToClipboard(JSON.stringify(payload, null, 2), btn);
|
|
1485
|
+
},
|
|
1486
|
+
/**
|
|
1487
|
+
* Internal UI helpers
|
|
1488
|
+
* @internal
|
|
1489
|
+
*
|
|
1490
|
+
* Copies an "abridged" view of results: just the selected cards with their
|
|
1491
|
+
* generator, origin, final score, and the top provenance reason. Designed
|
|
1492
|
+
* for pasting into bug reports without flooding with full provenance.
|
|
1493
|
+
*/
|
|
1494
|
+
_copyResults(runId, btn) {
|
|
1495
|
+
const run = runHistory.find((r) => r.runId === runId);
|
|
1496
|
+
if (!run) return;
|
|
1497
|
+
const selected = run.cards.filter((c) => c.selected).sort((a, b) => b.finalScore - a.finalScore).map((c) => ({
|
|
1498
|
+
cardId: c.cardId,
|
|
1499
|
+
generator: c.generator,
|
|
1500
|
+
origin: c.origin,
|
|
1501
|
+
score: Number(c.finalScore.toFixed(3)),
|
|
1502
|
+
topReason: c.provenance[0]?.reason ?? ""
|
|
1503
|
+
}));
|
|
1504
|
+
const payload = {
|
|
1505
|
+
runId: run.runId,
|
|
1506
|
+
label: run.hints?._label ?? null,
|
|
1507
|
+
finalCount: run.finalCount,
|
|
1508
|
+
newSelected: run.newSelected,
|
|
1509
|
+
reviewsSelected: run.reviewsSelected,
|
|
1510
|
+
selected
|
|
1511
|
+
};
|
|
1512
|
+
copyTextToClipboard(JSON.stringify(payload, null, 2), btn);
|
|
1513
|
+
},
|
|
1363
1514
|
/**
|
|
1364
1515
|
* Show help.
|
|
1365
1516
|
*/
|
|
@@ -5624,12 +5775,12 @@ ${e.stack}` : JSON.stringify(e);
|
|
|
5624
5775
|
async getWeightedCards(limit) {
|
|
5625
5776
|
const u = await this._getCurrentUser();
|
|
5626
5777
|
try {
|
|
5627
|
-
const
|
|
5778
|
+
const navigator2 = await this.createNavigator(u);
|
|
5628
5779
|
if (this._pendingHints) {
|
|
5629
|
-
|
|
5780
|
+
navigator2.setEphemeralHints(this._pendingHints);
|
|
5630
5781
|
this._pendingHints = null;
|
|
5631
5782
|
}
|
|
5632
|
-
return
|
|
5783
|
+
return navigator2.getWeightedCards(limit);
|
|
5633
5784
|
} catch (e) {
|
|
5634
5785
|
logger.error(`[courseDB] Error getting weighted cards: ${e}`);
|
|
5635
5786
|
throw e;
|