eklavya 1.45.0 → 1.46.0
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/README.md +11 -1
- package/dist/artifacts.js +26 -0
- package/dist/artifacts.js.map +1 -1
- package/dist/assets/artifact-template.html +21 -0
- package/dist/assets/dashboard.html +248 -28
- package/dist/assets/tutor/SKILL.md +2 -1
- package/dist/assets/tutor/references/writing-mcq.md +11 -1
- package/dist/cli.js +44 -12
- package/dist/cli.js.map +1 -1
- package/dist/dashboard.js +159 -47
- package/dist/dashboard.js.map +1 -1
- package/dist/hooks/checkpoint-quiz.js +3 -2
- package/dist/hooks/checkpoint-quiz.js.map +1 -1
- package/dist/hooks/memory-lib.js +1 -1
- package/dist/hooks/memory-lib.js.map +1 -1
- package/dist/hooks/stop-quiz-check.js +3 -2
- package/dist/hooks/stop-quiz-check.js.map +1 -1
- package/dist/memory/recall.js +1 -1
- package/dist/migrations/019_attempt_corrections.sql +41 -0
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/agents/explainer.md +9 -5
- package/dist/plugin/skills/tutor/SKILL.md +2 -1
- package/dist/plugin/skills/tutor/references/writing-mcq.md +11 -1
- package/dist/store.js +142 -14
- package/dist/store.js.map +1 -1
- package/dist/telemetry-send.js +9 -7
- package/dist/telemetry-send.js.map +1 -1
- package/dist/tools/get_learner_profile.js +5 -3
- package/dist/tools/get_learner_profile.js.map +1 -1
- package/dist/tools/get_session_quiz_plan.js +1 -1
- package/dist/tools/get_session_quiz_plan.js.map +1 -1
- package/dist/tools/record_attempt.js +48 -8
- package/dist/tools/record_attempt.js.map +1 -1
- package/dist/user-skill/eklavya/SKILL.md +7 -3
- package/dist/user-skill/eklavya-artifacts/SKILL.md +10 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,6 +48,14 @@ Code's learning loop.
|
|
|
48
48
|
| Maintain memory | `memory_write`, `memory_correct`, `memory_delete`, `memory_collections` |
|
|
49
49
|
| Inspect code | `code_outline`, `code_find_symbol` |
|
|
50
50
|
|
|
51
|
+
`record_attempt` returns the `attempt_id` of the row it wrote. For a
|
|
52
|
+
multiple-choice question, also pass `correct` (the right option's label,
|
|
53
|
+
verbatim), which lets the learner correct a missed answer from its explainer
|
|
54
|
+
page in the dashboard, where the server grades the new pick, and `option_notes`
|
|
55
|
+
(the note under each option, in order), which that page shows under each option. A `correct` that is not one of
|
|
56
|
+
the options, or notes of the wrong length, is stored as nothing and reported
|
|
57
|
+
back (`correct_mismatch`, `option_notes_mismatch`); the answer is still kept.
|
|
58
|
+
|
|
51
59
|
Search first, choose relevant entries, then use `memory_get` for their full
|
|
52
60
|
content. Pass `memory_get` the `receipt_id` a recall block names to link the
|
|
53
61
|
read to that recall. Calls to `memory_search`, `memory_get`, `memory_timeline`
|
|
@@ -68,7 +76,9 @@ npx eklavya db-path
|
|
|
68
76
|
```
|
|
69
77
|
|
|
70
78
|
The [CLI reference](https://eklavya-run.web.app/docs/cli/) lists all commands and
|
|
71
|
-
flags.
|
|
79
|
+
flags. The dashboard reads the database and has two guarded writes: a setting
|
|
80
|
+
change and a correction of a missed answer
|
|
81
|
+
([what it is, underneath](https://eklavya-run.web.app/docs/dashboard/#what-it-is-underneath)). [Configuration](https://eklavya-run.web.app/docs/configuration/) lists
|
|
72
82
|
defaults and scopes. The [memory guide](https://eklavya-run.web.app/docs/memory/)
|
|
73
83
|
covers retrieval, processing, providers, privacy and sync.
|
|
74
84
|
|
package/dist/artifacts.js
CHANGED
|
@@ -98,6 +98,7 @@ export function createArtifact(opts) {
|
|
|
98
98
|
'{{PROJECT_NAME}}': escHtml(path.basename(project) || project),
|
|
99
99
|
'{{KIND}}': kind,
|
|
100
100
|
'{{CONCEPT}}': escHtml(opts.concept ?? ''),
|
|
101
|
+
'{{ATTEMPT}}': opts.attempt ? String(opts.attempt) : '',
|
|
101
102
|
'{{CREATED}}': now.toISOString(),
|
|
102
103
|
'{{EYEBROW}}': kind === 'explainer' ? 'Explainer' : 'Artifact',
|
|
103
104
|
};
|
|
@@ -114,6 +115,10 @@ function meta(head, name) {
|
|
|
114
115
|
const m = re.exec(head);
|
|
115
116
|
return m ? unescHtml(m[1]) : null;
|
|
116
117
|
}
|
|
118
|
+
/** A positive integer, or null: an `--attempt` that is anything else links nothing. */
|
|
119
|
+
export function attemptId(raw) {
|
|
120
|
+
return raw && /^[1-9]\d{0,15}$/.test(raw) ? Number(raw) : null;
|
|
121
|
+
}
|
|
117
122
|
function unescHtml(s) {
|
|
118
123
|
return s.replace(/&(amp|lt|gt|quot|#39);/g, (_, e) => ({ amp: '&', lt: '<', gt: '>', quot: '"', '#39': "'" })[e]);
|
|
119
124
|
}
|
|
@@ -128,6 +133,26 @@ function readHead(file) {
|
|
|
128
133
|
fs.closeSync(fd);
|
|
129
134
|
}
|
|
130
135
|
}
|
|
136
|
+
/** The attempt a page was written for, from its head; null when none or unreadable. */
|
|
137
|
+
export function readAttempt(file) {
|
|
138
|
+
try {
|
|
139
|
+
return attemptId(meta(readHead(file), 'eklavya:attempt'));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** The dashboard id (`<folder>/<file>`) of a file inside the artifact root, or null. */
|
|
146
|
+
export function artifactIdOf(file, root = artifactsDir()) {
|
|
147
|
+
let id;
|
|
148
|
+
try {
|
|
149
|
+
id = path.relative(fs.realpathSync(root), fs.realpathSync(file)).split(path.sep).join('/');
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return resolveArtifact(id, root) ? id : null;
|
|
155
|
+
}
|
|
131
156
|
/**
|
|
132
157
|
* Every artifact on disk, newest first. A page with no Eklavya metadata — one
|
|
133
158
|
* copied in by hand — is still listed, titled from its `<title>` or filename.
|
|
@@ -173,6 +198,7 @@ export function listArtifacts(root = artifactsDir()) {
|
|
|
173
198
|
project: meta(head, 'eklavya:project') || null,
|
|
174
199
|
kind: kind === 'explainer' ? 'explainer' : 'artifact',
|
|
175
200
|
concept: meta(head, 'eklavya:concept') || null,
|
|
201
|
+
attempt: attemptId(meta(head, 'eklavya:attempt')),
|
|
176
202
|
created: meta(head, 'eklavya:created') || st.mtime.toISOString(),
|
|
177
203
|
bytes: st.size,
|
|
178
204
|
});
|
package/dist/artifacts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<meta name="eklavya:project" content="{{PROJECT}}">
|
|
9
9
|
<meta name="eklavya:kind" content="{{KIND}}">
|
|
10
10
|
<meta name="eklavya:concept" content="{{CONCEPT}}">
|
|
11
|
+
<meta name="eklavya:attempt" content="{{ATTEMPT}}">
|
|
11
12
|
<meta name="eklavya:created" content="{{CREATED}}">
|
|
12
13
|
<!-- Branding and share metadata. The icon is the fixed mark, inlined so the page
|
|
13
14
|
stays one file; og:image is only fetched by whatever the page is shared to. -->
|
|
@@ -98,6 +99,8 @@
|
|
|
98
99
|
ol.options li.right{border-color:var(--spot);background:var(--spot-soft)}
|
|
99
100
|
ol.options li.picked::after,ol.options li.right::after{display:block;margin-top:4px;
|
|
100
101
|
font:400 11px var(--font-mono);letter-spacing:var(--tracking-caps);text-transform:uppercase}
|
|
102
|
+
/* The one-line note under each option, as the question showed it. */
|
|
103
|
+
ol.options li .note{display:block;color:var(--dim);font-size:13px;margin-top:2px}
|
|
101
104
|
ol.options li.picked::after{content:"Your answer";color:var(--error)}
|
|
102
105
|
ol.options li.right::after{content:"Right answer";color:var(--spot)}
|
|
103
106
|
|
|
@@ -138,6 +141,9 @@
|
|
|
138
141
|
linear-gradient(90deg,var(--grid) 1px,transparent 1px);background-size:72px 72px}
|
|
139
142
|
main{background:var(--bg);border-left:1px solid var(--line-2);border-right:1px solid var(--line-2);
|
|
140
143
|
padding-left:40px;padding-right:40px;min-height:100vh}
|
|
144
|
+
/* In the dashboard's viewer the frame is as tall as the page, so a
|
|
145
|
+
viewport-high floor would grow it every time it reported its height. */
|
|
146
|
+
.framed main{min-height:0}
|
|
141
147
|
}
|
|
142
148
|
@media (max-width:640px){ main{padding:76px 16px 64px!important} .actions{top:12px;right:12px} }
|
|
143
149
|
|
|
@@ -171,6 +177,21 @@
|
|
|
171
177
|
<button type="button" onclick="saveHtml()" aria-label="Download this page as an HTML file">HTML</button>
|
|
172
178
|
</div>
|
|
173
179
|
<script>
|
|
180
|
+
/* Inside the dashboard's explainer viewer: report this page's height so the
|
|
181
|
+
viewer shows it as one document, and take the viewer's ground. Opened on its
|
|
182
|
+
own, it does neither. */
|
|
183
|
+
(function(){
|
|
184
|
+
if(window.parent===window) return;
|
|
185
|
+
var root=document.documentElement;
|
|
186
|
+
root.classList.add('framed');
|
|
187
|
+
function post(){ parent.postMessage({type:'eklavya:height',h:root.scrollHeight},'*'); }
|
|
188
|
+
addEventListener('load',post);
|
|
189
|
+
if(window.ResizeObserver) new ResizeObserver(post).observe(root);
|
|
190
|
+
addEventListener('message',function(e){
|
|
191
|
+
var d=e.data;
|
|
192
|
+
if(e.source===parent&&d&&d.type==='eklavya:mode'&&(d.mode==='ink'||d.mode==='paper')) root.setAttribute('data-mode',d.mode);
|
|
193
|
+
});
|
|
194
|
+
})();
|
|
174
195
|
function saveHtml(){
|
|
175
196
|
var name=(location.pathname.split('/').pop()||'').replace(/\.html?$/,'')||'artifact';
|
|
176
197
|
var blob=new Blob(['<!DOCTYPE html>\n'+document.documentElement.outerHTML],{type:'text/html'});
|
|
@@ -262,6 +262,53 @@
|
|
|
262
262
|
.art__thumb { display:block; aspect-ratio:16/10; background:var(--panel); border:1px solid var(--line-2); padding:var(--space-3); }
|
|
263
263
|
.art__open:hover .art__thumb { border-color:var(--spot); }
|
|
264
264
|
.art__thumb img { display:block; width:100%; height:100%; object-fit:contain; }
|
|
265
|
+
.tag { font-family:var(--font-mono); font-size:11px; letter-spacing:var(--tracking-caps); text-transform:uppercase; }
|
|
266
|
+
.tag.warn { color:var(--warning); }
|
|
267
|
+
.tag.ok { color:var(--spot); }
|
|
268
|
+
|
|
269
|
+
/* The explainer viewer: the agent's page, framed sandboxed, then this page's
|
|
270
|
+
own correction bar. The frame takes the page's reported height, so the two
|
|
271
|
+
scroll as one document; with no report it stays 80vh and scrolls inside. */
|
|
272
|
+
.viewer { display:block; width:100%; height:80vh; border:0; background:var(--bg); }
|
|
273
|
+
.fixbar { display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-2);
|
|
274
|
+
padding:var(--space-5); border:1px solid var(--line-2); background:var(--panel); }
|
|
275
|
+
.fixbar p { margin:0; }
|
|
276
|
+
.fixbar__note { color:var(--dim); font-size:13px; }
|
|
277
|
+
.fixbar__done { display:flex; align-items:center; gap:var(--space-2); color:var(--ink); }
|
|
278
|
+
.fixbar__done svg { width:16px; height:16px; color:var(--spot); flex:none; }
|
|
279
|
+
.btn { font-family:var(--font-mono); font-size:12px; letter-spacing:.06em; text-transform:uppercase;
|
|
280
|
+
background:var(--spot); color:var(--spot-ink); border:1px solid var(--spot); padding:10px 16px; cursor:pointer;
|
|
281
|
+
transition:background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease); }
|
|
282
|
+
.btn:hover { background:var(--mass); color:var(--spot); }
|
|
283
|
+
.btn:focus-visible, .fix__opt:focus-visible, .set__btn:focus-visible { outline:none; box-shadow:var(--ring); }
|
|
284
|
+
|
|
285
|
+
/* The correction modal: a native <dialog>, so focus, Escape and the top
|
|
286
|
+
layer come with it. Square, hairline, no shadow; the backdrop blurs. */
|
|
287
|
+
dialog.fix { width:min(560px, calc(100vw - 32px)); max-height:calc(100vh - 32px); overflow:auto; margin:auto;
|
|
288
|
+
padding:var(--space-6); border:1px solid var(--line-2); border-radius:0; background:var(--panel); color:var(--ink); }
|
|
289
|
+
dialog.fix::backdrop { background:color-mix(in srgb, var(--ink) 40%, transparent); backdrop-filter:blur(6px); }
|
|
290
|
+
dialog.fix[open] { animation:fix-in 180ms var(--ease); }
|
|
291
|
+
@keyframes fix-in { from { opacity:0; transform:translateY(8px); } }
|
|
292
|
+
.fix__eyebrow { margin:0 0 var(--space-3); font:400 11px/1 var(--font-mono); letter-spacing:var(--tracking-caps-wide);
|
|
293
|
+
text-transform:uppercase; color:var(--spot); }
|
|
294
|
+
.fix__q { margin:0 0 var(--space-5); font:800 clamp(20px,3vw,26px)/1.15 var(--font-disp); letter-spacing:var(--tracking-tight); }
|
|
295
|
+
.fix__opts { display:flex; flex-direction:column; gap:var(--space-2); }
|
|
296
|
+
.fix__opt { display:flex; flex-direction:column; gap:2px; text-align:left; width:100%; padding:10px 14px;
|
|
297
|
+
border:1px solid var(--line-2); background:var(--bg); color:var(--ink); font:inherit; cursor:pointer; }
|
|
298
|
+
.fix__opt:hover:not(:disabled) { border-color:var(--spot); }
|
|
299
|
+
.fix__opt:disabled { cursor:default; }
|
|
300
|
+
.fix__n { color:var(--dim); font-size:13px; }
|
|
301
|
+
.fix__tag { font:400 11px var(--font-mono); letter-spacing:var(--tracking-caps); text-transform:uppercase; }
|
|
302
|
+
.fix__tag:empty { display:none; }
|
|
303
|
+
.fix__opt.is-wrong { border-color:var(--error); }
|
|
304
|
+
.fix__opt.is-wrong .fix__tag { color:var(--error); }
|
|
305
|
+
.fix__opt.is-right { border-color:var(--spot); background:var(--spot-soft); }
|
|
306
|
+
.fix__opt.is-right .fix__tag { color:var(--spot); }
|
|
307
|
+
.fix__foot { display:flex; align-items:center; gap:var(--space-4); margin-top:var(--space-5); }
|
|
308
|
+
.fix__msg { flex:1; margin:0; font-size:13.5px; }
|
|
309
|
+
.fix__msg.wrong { color:var(--error); }
|
|
310
|
+
.fix__msg.warn { color:var(--warning); }
|
|
311
|
+
.fix__msg.ok { color:var(--spot); }
|
|
265
312
|
.g2 > *, .g3 > * { margin-top:0 !important; }
|
|
266
313
|
|
|
267
314
|
/* ---------- stat tiles ---------- */
|
|
@@ -720,6 +767,16 @@
|
|
|
720
767
|
</main>
|
|
721
768
|
</div>
|
|
722
769
|
|
|
770
|
+
<dialog class="fix" id="fix" aria-labelledby="fix-q">
|
|
771
|
+
<p class="fix__eyebrow">Correct your answer</p>
|
|
772
|
+
<h2 class="fix__q" id="fix-q"></h2>
|
|
773
|
+
<div class="fix__opts" id="fix-opts" role="group" aria-labelledby="fix-q"></div>
|
|
774
|
+
<div class="fix__foot">
|
|
775
|
+
<p class="fix__msg" id="fix-msg" role="status"></p>
|
|
776
|
+
<button type="button" class="set__btn" id="fix-close">Close</button>
|
|
777
|
+
</div>
|
|
778
|
+
</dialog>
|
|
779
|
+
|
|
723
780
|
<div class="picker" id="wf-menu" role="menu" aria-label="Workflows" hidden></div>
|
|
724
781
|
<div class="picker" id="proj-menu" role="listbox" aria-labelledby="proj-label" hidden></div>
|
|
725
782
|
<div class="picker" id="sel-menu" role="listbox" hidden></div>
|
|
@@ -832,14 +889,14 @@ const scopeName = () => (PROJECT === null ? 'all projects' : projectById(PROJECT
|
|
|
832
889
|
/** Inventory rows in scope — one, or all of them. */
|
|
833
890
|
const projectsInScope = () => INV.projects.filter((p) => PROJECT === null || p.id === PROJECT);
|
|
834
891
|
|
|
835
|
-
/** One row per calendar day in scope: { day, passed, missed, skipped, total }. */
|
|
892
|
+
/** One row per calendar day in scope: { day, passed, corrected, missed, skipped, total }. */
|
|
836
893
|
function dailyInScope() {
|
|
837
894
|
const m = new Map();
|
|
838
895
|
for (const d of S.daily) {
|
|
839
896
|
if (!inScope(d.repo)) continue;
|
|
840
|
-
const cur = m.get(d.day) ?? { day:d.day, passed:0, missed:0, skipped:0, total:0 };
|
|
841
|
-
cur.passed += d.passed; cur.missed += d.missed; cur.skipped += d.skipped;
|
|
842
|
-
cur.total = cur.passed + cur.missed + cur.skipped;
|
|
897
|
+
const cur = m.get(d.day) ?? { day:d.day, passed:0, corrected:0, missed:0, skipped:0, total:0 };
|
|
898
|
+
cur.passed += d.passed; cur.corrected += d.corrected ?? 0; cur.missed += d.missed; cur.skipped += d.skipped;
|
|
899
|
+
cur.total = cur.passed + cur.corrected + cur.missed + cur.skipped;
|
|
843
900
|
m.set(d.day, cur);
|
|
844
901
|
}
|
|
845
902
|
return m;
|
|
@@ -853,7 +910,7 @@ function fillDays(map, n, endKey) {
|
|
|
853
910
|
const cur = new Date(end.getTime() - (n - 1) * DAY_MS);
|
|
854
911
|
for (let i = 0; i < n; i++) {
|
|
855
912
|
const k = dayKey(cur);
|
|
856
|
-
out.push(map.get(k) ?? { day:k, passed:0, missed:0, skipped:0, total:0 });
|
|
913
|
+
out.push(map.get(k) ?? { day:k, passed:0, corrected:0, missed:0, skipped:0, total:0 });
|
|
857
914
|
cur.setUTCDate(cur.getUTCDate() + 1);
|
|
858
915
|
}
|
|
859
916
|
return out;
|
|
@@ -935,6 +992,8 @@ function sessionLabel(s) {
|
|
|
935
992
|
============================================================ */
|
|
936
993
|
const meter = (p, kind) => `<div class="meter${kind ? ' ' + kind : ''}"><i style="width:${Math.max(0, Math.min(100, p))}%"></i></div>`;
|
|
937
994
|
const gradeBadge = (a) => {
|
|
995
|
+
// A miss corrected from its explainer: the row stays, labelled for what it became.
|
|
996
|
+
if (a.corrected_at) return `<span class="grade pass">corrected${a.corrected_try ? ` · try ${a.corrected_try}` : ''}</span>`;
|
|
938
997
|
if (a.outcome === 'declined') return '<span class="grade">skipped</span>';
|
|
939
998
|
if (a.outcome === 'dont_know') return '<span class="grade">blanked</span>';
|
|
940
999
|
return `<span class="grade ${a.grade >= 3 ? 'pass' : 'miss'}">${a.grade}/5</span>`;
|
|
@@ -1084,7 +1143,11 @@ const slice = (arr, p, per = PER) => arr.slice((p - 1) * per, p * per);
|
|
|
1084
1143
|
Charts — hand-rolled SVG. No library, no network, both grounds.
|
|
1085
1144
|
============================================================ */
|
|
1086
1145
|
|
|
1087
|
-
/**
|
|
1146
|
+
/**
|
|
1147
|
+
* Stacked daily bars: right at the base, then corrected (a miss answered right
|
|
1148
|
+
* later from its explainer, counted once, on the day of the miss), then
|
|
1149
|
+
* missed, then skipped.
|
|
1150
|
+
*/
|
|
1088
1151
|
function chartActivity(el, days) {
|
|
1089
1152
|
const tip = el.parentElement.querySelector('.tip');
|
|
1090
1153
|
const W = Math.max(320, el.parentElement.clientWidth || 900);
|
|
@@ -1093,7 +1156,7 @@ function chartActivity(el, days) {
|
|
|
1093
1156
|
const step = (W - PAD_L) / days.length;
|
|
1094
1157
|
const bw = Math.min(26, Math.max(4, step - 5));
|
|
1095
1158
|
const y = (v) => (H - PAD_B) - (v / max) * (H - PAD_B - 10);
|
|
1096
|
-
const SEGMENTS = [['passed', 'var(--spot)'], ['missed', 'var(--warning)'], ['skipped', 'var(--faint-2)']];
|
|
1159
|
+
const SEGMENTS = [['passed', 'var(--spot)'], ['corrected', 'var(--spot-soft)'], ['missed', 'var(--warning)'], ['skipped', 'var(--faint-2)']];
|
|
1097
1160
|
|
|
1098
1161
|
let out = '';
|
|
1099
1162
|
for (let i = 0; i <= 2; i++) {
|
|
@@ -1109,7 +1172,9 @@ function chartActivity(el, days) {
|
|
|
1109
1172
|
if (!d[key]) continue;
|
|
1110
1173
|
const h = (d[key] / max) * (H - PAD_B - 10);
|
|
1111
1174
|
const inset = acc ? 2 : 0; // a 2px surface gap between stacked fills
|
|
1112
|
-
|
|
1175
|
+
// The soft fill gets a hairline in the accent, or it vanishes against the panel.
|
|
1176
|
+
const edge = key === 'corrected' ? ' stroke="var(--spot)" stroke-width="1"' : '';
|
|
1177
|
+
seg += `<rect x="${x}" y="${y(acc) - h + inset}" width="${bw}" height="${Math.max(1, h - inset)}" fill="${color}"${edge}/>`;
|
|
1113
1178
|
acc += d[key]; top = color;
|
|
1114
1179
|
}
|
|
1115
1180
|
out += `<rect class="hit" x="${PAD_L + i * step}" y="0" width="${step}" height="${H - PAD_B}" data-i="${i}" tabindex="0"/>`
|
|
@@ -1127,7 +1192,7 @@ function chartActivity(el, days) {
|
|
|
1127
1192
|
const show = () => {
|
|
1128
1193
|
const d = days[+hit.dataset.i];
|
|
1129
1194
|
tip.innerHTML = `<div class="mono">${esc(fmtDay(d.day))}</div>`
|
|
1130
|
-
+ [['Right', d.passed], ['Missed', d.missed], ['Skipped', d.skipped]]
|
|
1195
|
+
+ [['Right', d.passed], ['Corrected', d.corrected], ['Missed', d.missed], ['Skipped', d.skipped]]
|
|
1131
1196
|
.map(([k, v]) => `<div class="r"><span>${k}</span><span>${v}</span></div>`).join('');
|
|
1132
1197
|
const r = hit.getBoundingClientRect(), p = el.parentElement.getBoundingClientRect();
|
|
1133
1198
|
tip.style.left = (r.left - p.left + r.width / 2) + 'px';
|
|
@@ -1440,11 +1505,12 @@ function viewLearningDashboard() {
|
|
|
1440
1505
|
card('Answers, last 30 days', null,
|
|
1441
1506
|
`<div class="legend">
|
|
1442
1507
|
<span><b style="background:var(--spot)"></b>Right</span>
|
|
1508
|
+
<span><b style="background:var(--spot-soft);box-shadow:inset 0 0 0 1px var(--spot)"></b>Corrected</span>
|
|
1443
1509
|
<span><b style="background:var(--warning)"></b>Missed</span>
|
|
1444
1510
|
<span><b style="background:var(--faint-2)"></b>Skipped</span>
|
|
1445
1511
|
</div>
|
|
1446
1512
|
<div class="chart"><div class="tip" id="tip"></div><svg id="c-activity" role="img"
|
|
1447
|
-
aria-label="Daily answers, split into right, missed and skipped"></svg></div>`),
|
|
1513
|
+
aria-label="Daily answers, split into right, corrected, missed and skipped"></svg></div>`),
|
|
1448
1514
|
|
|
1449
1515
|
`<div class="g2">
|
|
1450
1516
|
${card('Weakest five', null,
|
|
@@ -1587,7 +1653,7 @@ function viewConcept(slug) {
|
|
|
1587
1653
|
|
|
1588
1654
|
tiles([
|
|
1589
1655
|
['Score', c.seen ? Math.round(c.score * 100) + '%' : '—', decayed ? `decayed from ${Math.round(c.stored_score * 100)}%` : 'last five grades, recent first', c.mastered ? 'good' : c.score < 0.5 && c.seen ? 'warn' : ''],
|
|
1590
|
-
['State', c.mastered ? 'mastered' : c.seen ? (c.due ? 'due' : 'learning') : 'not asked', c.seen ? `${c.passed} of ${c.attempts} right` : 'no questions yet', c.due ? 'warn' : ''],
|
|
1656
|
+
['State', c.mastered ? 'mastered' : c.seen ? (c.due ? 'due' : 'learning') : 'not asked', c.seen ? `${c.passed} of ${c.attempts} right${c.corrected ? ` · ${c.corrected} corrected` : ''}` : 'no questions yet', c.due ? 'warn' : ''],
|
|
1591
1657
|
['Next review', c.owed ? whenDue(c.next_review) : '—', c.interval_d ? `every ${Math.round(c.interval_d)}d · ease ${c.ease ? c.ease.toFixed(2) : '—'} · ${plural(c.reps, 'rep')}` : 'not scheduled', c.due ? 'warn' : ''],
|
|
1592
1658
|
]),
|
|
1593
1659
|
|
|
@@ -1856,9 +1922,9 @@ function viewProjects() {
|
|
|
1856
1922
|
const m = new Map();
|
|
1857
1923
|
for (const d of S.daily) {
|
|
1858
1924
|
if (pid(d.repo) !== p.id) continue;
|
|
1859
|
-
const cur = m.get(d.day) ?? { day:d.day, passed:0, missed:0, skipped:0, total:0 };
|
|
1860
|
-
cur.passed += d.passed; cur.missed += d.missed; cur.skipped += d.skipped;
|
|
1861
|
-
cur.total = cur.passed + cur.missed + cur.skipped;
|
|
1925
|
+
const cur = m.get(d.day) ?? { day:d.day, passed:0, corrected:0, missed:0, skipped:0, total:0 };
|
|
1926
|
+
cur.passed += d.passed; cur.corrected += d.corrected ?? 0; cur.missed += d.missed; cur.skipped += d.skipped;
|
|
1927
|
+
cur.total = cur.passed + cur.corrected + cur.missed + cur.skipped;
|
|
1862
1928
|
m.set(d.day, cur);
|
|
1863
1929
|
}
|
|
1864
1930
|
chartActivity(el, fillDays(m, 30));
|
|
@@ -2462,8 +2528,10 @@ function viewHealth() {
|
|
|
2462
2528
|
Artifacts — the pages an agent wrote under ~/.eklavya/artifacts/.
|
|
2463
2529
|
`S.artifacts` is read from the files on every load; opening one is a plain
|
|
2464
2530
|
link to `/artifacts/<id>`, which the server serves sandboxed, in a new tab.
|
|
2531
|
+
An explainer for a missed question that can still be corrected opens in
|
|
2532
|
+
the viewer instead (`#/artifacts/view/<id>`), with the correction bar.
|
|
2465
2533
|
============================================================ */
|
|
2466
|
-
const ARTIFACT_KINDS = ['all', 'explainer', 'artifact'];
|
|
2534
|
+
const ARTIFACT_KINDS = ['all', 'explainer', 'artifact', 'to-correct'];
|
|
2467
2535
|
const artifactsInScope = () => (S.artifacts ?? []).filter((a) => inScope(a.project));
|
|
2468
2536
|
const artifactUrl = (a) => '/artifacts/' + a.id.split('/').map(encodeURIComponent).join('/');
|
|
2469
2537
|
|
|
@@ -2475,12 +2543,17 @@ function artifactCard(a) {
|
|
|
2475
2543
|
const concept = !a.concept ? ''
|
|
2476
2544
|
: known ? ` · <a class="link" data-go="${esc(LH('concept', a.concept))}">${esc(a.concept)}</a>`
|
|
2477
2545
|
: ` · ${esc(a.concept)}`;
|
|
2546
|
+
const tag = a.correction === 'open' ? ' · <span class="tag warn">To correct</span>'
|
|
2547
|
+
: a.correction === 'done' ? ' · <span class="tag ok">Corrected</span>' : '';
|
|
2548
|
+
// A correctable explainer opens in the viewer, where the correction lives;
|
|
2549
|
+
// every other page is a plain link to the sandboxed file, in a new tab.
|
|
2550
|
+
const open = a.correction ? `href="${esc(AH('view', a.id))}"` : `href="${esc(artifactUrl(a))}" target="_blank" rel="noopener"`;
|
|
2478
2551
|
return `<article class="art">
|
|
2479
|
-
<a class="art__open"
|
|
2552
|
+
<a class="art__open" ${open}>
|
|
2480
2553
|
<span class="art__thumb"><img src="${esc(thumbUrl(a, window.eklavyaGround.read()))}" data-thumb="${esc(thumbUrl(a, '@'))}" alt="" loading="lazy" decoding="async"></span>
|
|
2481
2554
|
<span class="title">${esc(a.title)}</span></a>
|
|
2482
2555
|
${a.description ? `<p class="ctx">${esc(a.description)}</p>` : ''}
|
|
2483
|
-
<p class="slug">${esc(a.kind)} · ${esc(stamp(a.created))}${concept}</p>
|
|
2556
|
+
<p class="slug">${esc(a.kind)} · ${esc(stamp(a.created))}${concept}${tag}</p>
|
|
2484
2557
|
</article>`;
|
|
2485
2558
|
}
|
|
2486
2559
|
|
|
@@ -2490,7 +2563,7 @@ function viewArtifacts(param) {
|
|
|
2490
2563
|
const all = artifactsInScope();
|
|
2491
2564
|
const q = t.q.trim().toLowerCase();
|
|
2492
2565
|
const list = all.filter((a) => {
|
|
2493
|
-
if (kind !== 'all' && a.kind !== kind) return false;
|
|
2566
|
+
if (kind === 'to-correct' ? a.correction !== 'open' : kind !== 'all' && a.kind !== kind) return false;
|
|
2494
2567
|
if (q && ![a.title, a.description, a.concept ?? '', pname(a.project), a.id].join(' ').toLowerCase().includes(q)) return false;
|
|
2495
2568
|
return true;
|
|
2496
2569
|
});
|
|
@@ -2504,6 +2577,7 @@ function viewArtifacts(param) {
|
|
|
2504
2577
|
}
|
|
2505
2578
|
const counts = { all: all.length, explainer: all.filter((a) => a.kind === 'explainer').length };
|
|
2506
2579
|
counts.artifact = all.length - counts.explainer;
|
|
2580
|
+
counts['to-correct'] = all.filter((a) => a.correction === 'open').length;
|
|
2507
2581
|
const projects = new Set(all.map((a) => pid(a.project))).size;
|
|
2508
2582
|
const chip = (k, label) => `<button class="chip" data-go="${esc(AH('dashboard', k === 'all' ? null : k))}" aria-pressed="${kind === k}">${label} ${counts[k]}</button>`;
|
|
2509
2583
|
const body = !list.length
|
|
@@ -2516,16 +2590,150 @@ function viewArtifacts(param) {
|
|
|
2516
2590
|
plural(all.length, 'page'), plural(counts.explainer, 'explainer'),
|
|
2517
2591
|
`<a class="link" data-go="${esc(AH('projects'))}">${plural(projects, 'project')}</a>`,
|
|
2518
2592
|
], `Pages your agent wrote for you — explainers after a missed question, and anything you asked to have
|
|
2519
|
-
turned into a page. Each opens in a new tab, from <code>~/.eklavya/artifacts/</code
|
|
2593
|
+
turned into a page. Each opens in a new tab, from <code>~/.eklavya/artifacts/</code>. An explainer for a
|
|
2594
|
+
missed question opens here instead, with a <b>Correct your answer</b> button under it: pick the right
|
|
2595
|
+
option and the miss shows as corrected. That counts as a pass for the concept's score, but not toward a
|
|
2596
|
+
commit gate or a level, and the concept still comes back for review.`),
|
|
2520
2597
|
card(null, null,
|
|
2521
2598
|
`<div class="filters">
|
|
2522
2599
|
<div class="grow"><input type="search" id="aq" value="${esc(t.q)}" placeholder="Search artifacts" aria-label="Search artifacts"></div>
|
|
2523
2600
|
</div>
|
|
2524
|
-
<div class="chips">${chip('all','All')}${chip('explainer','Explainers')}${chip('artifact','Other pages')}</div>
|
|
2601
|
+
<div class="chips">${chip('all','All')}${chip('explainer','Explainers')}${chip('to-correct','To correct')}${chip('artifact','Other pages')}</div>
|
|
2525
2602
|
<div style="margin-top:var(--space-4)">${body}</div>`),
|
|
2526
2603
|
].join('');
|
|
2527
2604
|
}
|
|
2528
2605
|
|
|
2606
|
+
/* ---------- the explainer viewer and its correction ----------
|
|
2607
|
+
The agent's page is framed sandboxed without same-origin, so it can never
|
|
2608
|
+
read or write the API; the bar and the modal around it are this page's own
|
|
2609
|
+
code, and the server grades every pick. */
|
|
2610
|
+
const FRAME_SANDBOX = 'allow-scripts allow-modals allow-downloads allow-popups allow-popups-to-escape-sandbox';
|
|
2611
|
+
/** The correction on screen: `{ attempt, artifact, q }`, `q` being /api/attempts/correction's answer. */
|
|
2612
|
+
let FIX = null;
|
|
2613
|
+
|
|
2614
|
+
function viewArtifact(id) {
|
|
2615
|
+
const a = (S.artifacts ?? []).find((x) => x.id === id);
|
|
2616
|
+
if (!a) return viewNotFound(id, false);
|
|
2617
|
+
if (a.correction) fill('fixbar', `/api/attempts/correction?id=${a.attempt}`, (d) => fixBar(a, d));
|
|
2618
|
+
// The page's own ground follows this one; a page made before the viewer ignores it.
|
|
2619
|
+
AFTER.push(() => {
|
|
2620
|
+
const f = $('art-frame');
|
|
2621
|
+
f?.addEventListener('load', () => f.contentWindow?.postMessage({ type:'eklavya:mode', mode:window.eklavyaGround.read() }, '*'));
|
|
2622
|
+
});
|
|
2623
|
+
return `<a class="page__back" data-go="${esc(AH('dashboard'))}">← Artifacts</a>
|
|
2624
|
+
<iframe class="viewer" id="art-frame" src="${esc(artifactUrl(a))}?embed" title="${esc(a.title)}" sandbox="${FRAME_SANDBOX}"></iframe>
|
|
2625
|
+
${a.correction ? `<div class="fixbar" id="fixbar">${loader('Loading the question…')}</div>` : ''}`;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
function fixBar(a, d) {
|
|
2629
|
+
FIX = { attempt: a.attempt, artifact: a.id, q: d };
|
|
2630
|
+
const done = d.corrected_at !== null;
|
|
2631
|
+
armLeave(!done);
|
|
2632
|
+
return done
|
|
2633
|
+
? `<p class="fixbar__done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>Corrected on try ${d.tries} · ${esc(stamp(d.corrected_at))}</p>`
|
|
2634
|
+
: `<button type="button" class="btn" id="fix-open">Correct your answer</button>
|
|
2635
|
+
<p class="fixbar__note">Not now? Correct it later from Dashboard → Artifacts.</p>`;
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
/** The options in a new order each time: the page above shows the right one at its letter. */
|
|
2639
|
+
function shuffled(n) {
|
|
2640
|
+
const order = [...Array(n).keys()];
|
|
2641
|
+
for (let tries = 0; tries < 5; tries++) {
|
|
2642
|
+
for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [order[i], order[j]] = [order[j], order[i]]; }
|
|
2643
|
+
if (order.some((v, i) => v !== i)) break;
|
|
2644
|
+
}
|
|
2645
|
+
return order;
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
function fixSay(text, kind) {
|
|
2649
|
+
const m = $('fix-msg');
|
|
2650
|
+
m.textContent = text;
|
|
2651
|
+
m.className = 'fix__msg' + (kind ? ' ' + kind : '');
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
function openFix() {
|
|
2655
|
+
const d = FIX.q;
|
|
2656
|
+
$('fix-q').textContent = d.question;
|
|
2657
|
+
$('fix-opts').innerHTML = shuffled(d.options.length).map((i) =>
|
|
2658
|
+
`<button type="button" class="fix__opt" data-pick="${esc(d.options[i])}"><span>${esc(d.options[i])}</span>`
|
|
2659
|
+
+ `${d.option_notes?.[i] ? `<span class="fix__n">${esc(d.option_notes[i])}</span>` : ''}<span class="fix__tag"></span></button>`).join('');
|
|
2660
|
+
fixSay('', '');
|
|
2661
|
+
$('fix').showModal();
|
|
2662
|
+
$('fix-opts').querySelector('button').focus();
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
function pickFix(btn) {
|
|
2666
|
+
const all = [...$('fix-opts').querySelectorAll('button')];
|
|
2667
|
+
const reopen = () => all.forEach((b) => { b.disabled = !!b.dataset.wrong; });
|
|
2668
|
+
all.forEach((b) => { b.disabled = true; });
|
|
2669
|
+
postJson('/api/attempts/retry', { attempt_id: FIX.attempt, picked: btn.dataset.pick })
|
|
2670
|
+
.then((r) => {
|
|
2671
|
+
if (r.corrected) {
|
|
2672
|
+
btn.classList.add('is-right');
|
|
2673
|
+
btn.querySelector('.fix__tag').textContent = 'Right answer';
|
|
2674
|
+
fixSay('Corrected. Your stats now count this as answered.', 'ok');
|
|
2675
|
+
FIX.q = { ...FIX.q, tries: r.tries, corrected_at: new Date().toISOString() };
|
|
2676
|
+
const row = S.artifacts.find((x) => x.id === FIX.artifact);
|
|
2677
|
+
if (row) row.correction = 'done';
|
|
2678
|
+
// Every other count on the page was drawn from the payload it booted with:
|
|
2679
|
+
// say so now rather than waiting for the poll.
|
|
2680
|
+
$('stale').hidden = false;
|
|
2681
|
+
armLeave(false);
|
|
2682
|
+
$('fix-close').focus();
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
btn.classList.add('is-wrong');
|
|
2686
|
+
btn.dataset.wrong = '1';
|
|
2687
|
+
btn.querySelector('.fix__tag').textContent = 'Not this one';
|
|
2688
|
+
reopen();
|
|
2689
|
+
fixSay('Still incorrect. Please correct your answer.', 'wrong');
|
|
2690
|
+
all.find((b) => !b.disabled)?.focus();
|
|
2691
|
+
})
|
|
2692
|
+
.catch((e) => { reopen(); fixSay(e.message, 'warn'); });
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
$('fix-opts').addEventListener('click', (e) => {
|
|
2696
|
+
const b = e.target.closest('.fix__opt');
|
|
2697
|
+
if (b && !b.disabled) pickFix(b);
|
|
2698
|
+
});
|
|
2699
|
+
// Arrow keys walk the options that can still be picked; Enter and Space pick (native buttons).
|
|
2700
|
+
$('fix-opts').addEventListener('keydown', (e) => {
|
|
2701
|
+
const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[e.key];
|
|
2702
|
+
if (!step) return;
|
|
2703
|
+
e.preventDefault();
|
|
2704
|
+
const live = [...$('fix-opts').querySelectorAll('button:not(:disabled)')];
|
|
2705
|
+
const i = live.indexOf(document.activeElement);
|
|
2706
|
+
live[(i + step + live.length) % live.length]?.focus();
|
|
2707
|
+
});
|
|
2708
|
+
$('fix-close').addEventListener('click', () => $('fix').close());
|
|
2709
|
+
// However it closes -- the button, Escape -- the bar shows where things stand.
|
|
2710
|
+
$('fix').addEventListener('close', () => {
|
|
2711
|
+
const bar = $('fixbar');
|
|
2712
|
+
const a = FIX && S.artifacts.find((x) => x.id === FIX.artifact);
|
|
2713
|
+
if (bar && a) { bar.innerHTML = fixBar(a, FIX.q); $('fix-open')?.focus(); }
|
|
2714
|
+
});
|
|
2715
|
+
$('view').addEventListener('click', (e) => { if (e.target.closest('#fix-open')) openFix(); });
|
|
2716
|
+
|
|
2717
|
+
/**
|
|
2718
|
+
* Closing the tab with a correction still open asks first. Browsers show their
|
|
2719
|
+
* own generic prompt -- a page cannot set its text -- so the bar carries ours.
|
|
2720
|
+
*/
|
|
2721
|
+
const holdLeave = (e) => { e.preventDefault(); e.returnValue = ''; };
|
|
2722
|
+
let LEAVE = false;
|
|
2723
|
+
function armLeave(on) {
|
|
2724
|
+
if (on === LEAVE) return;
|
|
2725
|
+
LEAVE = on;
|
|
2726
|
+
window[on ? 'addEventListener' : 'removeEventListener']('beforeunload', holdLeave);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
// The framed page reports its height; only that frame's message counts.
|
|
2730
|
+
addEventListener('message', (e) => {
|
|
2731
|
+
const f = $('art-frame');
|
|
2732
|
+
if (!f || e.source !== f.contentWindow || e.data?.type !== 'eklavya:height') return;
|
|
2733
|
+
const h = Number(e.data.h);
|
|
2734
|
+
if (Number.isFinite(h)) f.style.height = Math.min(20000, Math.max(200, h)) + 'px';
|
|
2735
|
+
});
|
|
2736
|
+
|
|
2529
2737
|
function viewArtifactProjects() {
|
|
2530
2738
|
const by = new Map();
|
|
2531
2739
|
for (const a of artifactsInScope()) {
|
|
@@ -2730,6 +2938,20 @@ function controlValue(f, el) {
|
|
|
2730
2938
|
return el.value;
|
|
2731
2939
|
}
|
|
2732
2940
|
|
|
2941
|
+
/**
|
|
2942
|
+
* Every write the page makes. The server's `acceptWrite` wants the per-start
|
|
2943
|
+
* token from the page's own meta tag and JSON; this sends both. Resolves with
|
|
2944
|
+
* the parsed reply, rejects with the server's own error text.
|
|
2945
|
+
*/
|
|
2946
|
+
function postJson(path, body) {
|
|
2947
|
+
const token = document.querySelector('meta[name="eklavya-token"]')?.content ?? '';
|
|
2948
|
+
return fetch(path, {
|
|
2949
|
+
method: 'POST',
|
|
2950
|
+
headers: { 'content-type': 'application/json', 'x-eklavya-token': token },
|
|
2951
|
+
body: JSON.stringify(body),
|
|
2952
|
+
}).then((r) => r.json().then((d) => { if (!r.ok) throw new Error(d.error || `${r.status}`); return d; }));
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2733
2955
|
/**
|
|
2734
2956
|
* Saves one setting and redraws the settings in place — not through `render()`,
|
|
2735
2957
|
* which would scroll to the top and lose the control the reader just used.
|
|
@@ -2740,14 +2962,8 @@ function saveSetting(key, change) {
|
|
|
2740
2962
|
const say = (text, warn) => { if (msg) { msg.textContent = text; msg.className = warn ? 'warn' : 'here'; } };
|
|
2741
2963
|
fieldError(key, null);
|
|
2742
2964
|
say('saving…');
|
|
2743
|
-
const token = document.querySelector('meta[name="eklavya-token"]')?.content ?? '';
|
|
2744
2965
|
const gen = GEN;
|
|
2745
|
-
|
|
2746
|
-
method: 'POST',
|
|
2747
|
-
headers: { 'content-type': 'application/json', 'x-eklavya-token': token },
|
|
2748
|
-
body: JSON.stringify({ scope, project: SETS?.project?.id, key, ...change }),
|
|
2749
|
-
})
|
|
2750
|
-
.then((r) => r.json().then((d) => { if (!r.ok) throw new Error(d.error || `${r.status}`); }))
|
|
2966
|
+
postJson('/api/settings', { scope, project: SETS?.project?.id, key, ...change })
|
|
2751
2967
|
.then(() => fetch(settingsUrl()).then((r) => r.json()))
|
|
2752
2968
|
.then((d) => {
|
|
2753
2969
|
const el = $('settings');
|
|
@@ -2848,6 +3064,7 @@ const WORKFLOWS = {
|
|
|
2848
3064
|
],
|
|
2849
3065
|
pages: {
|
|
2850
3066
|
dashboard: { label: 'Dashboard', view: (p) => viewArtifacts(p) },
|
|
3067
|
+
view: { parent: 'dashboard', view: (p) => viewArtifact(p) },
|
|
2851
3068
|
projects: { label: 'Projects', view: () => viewArtifactProjects() },
|
|
2852
3069
|
},
|
|
2853
3070
|
},
|
|
@@ -3470,6 +3687,8 @@ function render(keepFocus) {
|
|
|
3470
3687
|
|
|
3471
3688
|
AFTER.length = 0;
|
|
3472
3689
|
LAZY.clear();
|
|
3690
|
+
// A screen that still has a correction open re-arms this when it draws its bar.
|
|
3691
|
+
armLeave(false);
|
|
3473
3692
|
$('view').innerHTML = !pg ? viewNotFound('#/' + CUR.wf + '/' + CUR.page, false)
|
|
3474
3693
|
: CUR.bad ? viewNotFound(CUR.param, true)
|
|
3475
3694
|
: pg.view(CUR.param);
|
|
@@ -3610,7 +3829,8 @@ addEventListener('hashchange', () => { if (INV) { closePicker(false); syncFromUr
|
|
|
3610
3829
|
let resizeTimer = 0;
|
|
3611
3830
|
addEventListener('resize', () => {
|
|
3612
3831
|
clearTimeout(resizeTimer);
|
|
3613
|
-
|
|
3832
|
+
// Not the viewer: a re-render would reload the framed page and lose its scroll.
|
|
3833
|
+
resizeTimer = setTimeout(() => { if (S && CUR.page !== 'view') render({ id:'q', pos:null }); }, 120);
|
|
3614
3834
|
});
|
|
3615
3835
|
|
|
3616
3836
|
document.querySelectorAll('[data-ground]').forEach((b) => {
|
|
@@ -183,7 +183,8 @@ scales, how long feedback may be, the sequence a blank earns, and what
|
|
|
183
183
|
`already_taught` changes.
|
|
184
184
|
|
|
185
185
|
**A missed answer can come back with `explain`.** That is `explain_on_wrong` at
|
|
186
|
-
work: follow its `instruction` exactly.
|
|
186
|
+
work: follow its `instruction` exactly. Hand the explainer its `attempt_id` and
|
|
187
|
+
`option_notes` too: the page shows the notes and can be corrected. The page is written in the background
|
|
187
188
|
and opens by itself, so give the verdict, say the page is on its way, and go
|
|
188
189
|
back to the task — no longer explanation, no waiting. When the developer asks
|
|
189
190
|
for something to be explained as a page ("explain this to me", "make me a page
|