Spaces:
Running
Running
Deploy conversion @ 960f4e5-space
Browse files
app.js
CHANGED
|
@@ -25,7 +25,7 @@ import { oauthLoginUrl, oauthHandleRedirectIfPresent, uploadFiles, downloadFile,
|
|
| 25 |
import { keyOf, latestByKey, mergeEvents, orderItems, firstUngraded as
|
| 26 |
firstUngradedPure, countComplete, stampClass, nextEpoch,
|
| 27 |
standsDownTo, statusOf, classifyProbe, hasScopes }
|
| 28 |
-
from "./logic.mjs?v=
|
| 29 |
|
| 30 |
// Test seam: Playwright installs window.__testHub (an in-memory hub) before
|
| 31 |
// any page script runs; production never defines it, so this is one inert
|
|
@@ -104,7 +104,7 @@ const state = {
|
|
| 104 |
breakShownAt: Date.now(), stickyBanner: null, stickyActions: [], stickyKind: null,
|
| 105 |
gradedSinceBreak: 0, infoNavsLeft: 0,
|
| 106 |
hbTimer: null, pushing: null, pushQueued: false, publishBlocked: null,
|
| 107 |
-
onPushSuccess: null,
|
| 108 |
};
|
| 109 |
|
| 110 |
const logKey = () => `audit:${CONFIG.task}:${state.user}`;
|
|
@@ -438,6 +438,16 @@ function renderItem() {
|
|
| 438 |
banner("");
|
| 439 |
|
| 440 |
if (state.idx >= state.order.length) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
main.append(el("h2", null, "All items graded — thank you!"));
|
| 442 |
const status = el("p", null, "Publishing your final save…");
|
| 443 |
status.setAttribute("role", "status");
|
|
@@ -573,13 +583,17 @@ function renderControls(item) {
|
|
| 573 |
}
|
| 574 |
|
| 575 |
function choose(name, value) {
|
|
|
|
| 576 |
selection[name] = value;
|
| 577 |
CONFIG.fields.forEach((field, fi) => {
|
| 578 |
groups[fi].querySelectorAll("button").forEach((button) => {
|
| 579 |
button.classList.toggle("sel", selection[field.name] === button.dataset.v);
|
| 580 |
});
|
| 581 |
});
|
| 582 |
-
|
|
|
|
|
|
|
|
|
|
| 583 |
}
|
| 584 |
|
| 585 |
// The one recording path: leaving an item (nav, save, tab-hide) commits a
|
|
@@ -632,7 +646,18 @@ document.addEventListener("keydown", (event) => {
|
|
| 632 |
return;
|
| 633 |
}
|
| 634 |
const binding = CONFIG.hotkeys[event.key];
|
| 635 |
-
if (binding) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 636 |
});
|
| 637 |
|
| 638 |
document.addEventListener("visibilitychange", () => {
|
|
|
|
| 25 |
import { keyOf, latestByKey, mergeEvents, orderItems, firstUngraded as
|
| 26 |
firstUngradedPure, countComplete, stampClass, nextEpoch,
|
| 27 |
standsDownTo, statusOf, classifyProbe, hasScopes }
|
| 28 |
+
from "./logic.mjs?v=960f4e5-space";
|
| 29 |
|
| 30 |
// Test seam: Playwright installs window.__testHub (an in-memory hub) before
|
| 31 |
// any page script runs; production never defines it, so this is one inert
|
|
|
|
| 104 |
breakShownAt: Date.now(), stickyBanner: null, stickyActions: [], stickyKind: null,
|
| 105 |
gradedSinceBreak: 0, infoNavsLeft: 0,
|
| 106 |
hbTimer: null, pushing: null, pushQueued: false, publishBlocked: null,
|
| 107 |
+
onPushSuccess: null, lastVerdictKey: null, lastVerdictAt: 0,
|
| 108 |
};
|
| 109 |
|
| 110 |
const logKey = () => `audit:${CONFIG.task}:${state.user}`;
|
|
|
|
| 438 |
banner("");
|
| 439 |
|
| 440 |
if (state.idx >= state.order.length) {
|
| 441 |
+
const pos = firstUngraded();
|
| 442 |
+
if (pos < state.order.length) {
|
| 443 |
+
// Not actually done — walking past the last item must never claim
|
| 444 |
+
// completion. Jump back to the first ungraded item instead.
|
| 445 |
+
state.idx = pos;
|
| 446 |
+
renderItem();
|
| 447 |
+
banner(`Not finished yet — jumped back to the next ungraded item.`,
|
| 448 |
+
false, [], "info");
|
| 449 |
+
return;
|
| 450 |
+
}
|
| 451 |
main.append(el("h2", null, "All items graded — thank you!"));
|
| 452 |
const status = el("p", null, "Publishing your final save…");
|
| 453 |
status.setAttribute("role", "status");
|
|
|
|
| 583 |
}
|
| 584 |
|
| 585 |
function choose(name, value) {
|
| 586 |
+
const wasComplete = CONFIG.fields.every((field) => selection[field.name]);
|
| 587 |
selection[name] = value;
|
| 588 |
CONFIG.fields.forEach((field, fi) => {
|
| 589 |
groups[fi].querySelectorAll("button").forEach((button) => {
|
| 590 |
button.classList.toggle("sel", selection[field.name] === button.dataset.v);
|
| 591 |
});
|
| 592 |
});
|
| 593 |
+
// Advance only when this choice COMPLETES the item. A revisited item is
|
| 594 |
+
// already complete, so changing one answer must never jump away — the
|
| 595 |
+
// change commits on j/k/s/tab-hide via _commitIfDirty.
|
| 596 |
+
if (!wasComplete && CONFIG.fields.every((field) => selection[field.name])) move(1);
|
| 597 |
}
|
| 598 |
|
| 599 |
// The one recording path: leaving an item (nav, save, tab-hide) commits a
|
|
|
|
| 646 |
return;
|
| 647 |
}
|
| 648 |
const binding = CONFIG.hotkeys[event.key];
|
| 649 |
+
if (binding) {
|
| 650 |
+
event.preventDefault();
|
| 651 |
+
// Double-press bounce: the SAME key repeating into the next item's first
|
| 652 |
+
// 150ms is the keystroke that just advanced the previous item. A
|
| 653 |
+
// different key (or a deliberate press later) always lands.
|
| 654 |
+
const now = Date.now();
|
| 655 |
+
if (event.key === state.lastVerdictKey && now - state.shownAt < 150
|
| 656 |
+
&& now - (state.lastVerdictAt ?? 0) < 300) return;
|
| 657 |
+
state.lastVerdictKey = event.key;
|
| 658 |
+
state.lastVerdictAt = now;
|
| 659 |
+
controls._choose?.(binding[0], binding[1]);
|
| 660 |
+
}
|
| 661 |
});
|
| 662 |
|
| 663 |
document.addEventListener("visibilitychange", () => {
|
config.js
CHANGED
|
@@ -2,7 +2,7 @@ window.CONFIG = {
|
|
| 2 |
task: "conversion",
|
| 3 |
title: "Pass 2 · rewrite fidelity — 152 items",
|
| 4 |
itemsPath: "conversion_items.json",
|
| 5 |
-
build: "
|
| 6 |
landing: "You will judge whether each rewritten exam question still asks the same "
|
| 7 |
+ "question as the original, and whether it stands on its own without the answer "
|
| 8 |
+ "options. 152 items plus 3 discussable warm-ups; expect roughly 1.5–2 hours, "
|
|
|
|
| 2 |
task: "conversion",
|
| 3 |
title: "Pass 2 · rewrite fidelity — 152 items",
|
| 4 |
itemsPath: "conversion_items.json",
|
| 5 |
+
build: "960f4e5-space",
|
| 6 |
landing: "You will judge whether each rewritten exam question still asks the same "
|
| 7 |
+ "question as the original, and whether it stands on its own without the answer "
|
| 8 |
+ "options. 152 items plus 3 discussable warm-ups; expect roughly 1.5–2 hours, "
|
index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; style-src-attr 'unsafe-inline'; font-src 'self'; connect-src https://huggingface.co https://*.hf.co; img-src 'self' data:; base-uri 'none'; form-action 'none'">
|
| 7 |
<title>LocalGate audit</title>
|
| 8 |
-
<link rel="stylesheet" href="style.css?v=
|
| 9 |
-
<link rel="stylesheet" href="katex/katex.min.css?v=
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<header>
|
|
@@ -22,9 +22,9 @@
|
|
| 22 |
<pre id="rubric-body"></pre></details>
|
| 23 |
</div>
|
| 24 |
<div id="controls" hidden></div>
|
| 25 |
-
<script src="katex/katex.min.js?v=
|
| 26 |
-
<script src="katex/auto-render.min.js?v=
|
| 27 |
-
<script src="config.js?v=
|
| 28 |
-
<script type="module" src="app.js?v=
|
| 29 |
</body>
|
| 30 |
</html>
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; style-src-attr 'unsafe-inline'; font-src 'self'; connect-src https://huggingface.co https://*.hf.co; img-src 'self' data:; base-uri 'none'; form-action 'none'">
|
| 7 |
<title>LocalGate audit</title>
|
| 8 |
+
<link rel="stylesheet" href="style.css?v=960f4e5-space">
|
| 9 |
+
<link rel="stylesheet" href="katex/katex.min.css?v=960f4e5-space">
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<header>
|
|
|
|
| 22 |
<pre id="rubric-body"></pre></details>
|
| 23 |
</div>
|
| 24 |
<div id="controls" hidden></div>
|
| 25 |
+
<script src="katex/katex.min.js?v=960f4e5-space"></script>
|
| 26 |
+
<script src="katex/auto-render.min.js?v=960f4e5-space"></script>
|
| 27 |
+
<script src="config.js?v=960f4e5-space"></script>
|
| 28 |
+
<script type="module" src="app.js?v=960f4e5-space"></script>
|
| 29 |
</body>
|
| 30 |
</html>
|
style.css
CHANGED
|
@@ -206,8 +206,11 @@ details#rubric pre { white-space: pre-wrap; font: inherit; margin: 0;
|
|
| 206 |
background: var(--field); z-index: 1;
|
| 207 |
margin: -.8rem -1.1rem .4rem; padding: .8rem 1.1rem .4rem; }
|
| 208 |
|
| 209 |
-
/* key hints are noise on touch screens (either signal suffices)
|
| 210 |
-
|
|
|
|
|
|
|
|
|
|
| 211 |
|
| 212 |
/* KaTeX inherits our reading size; display math gets breathing room */
|
| 213 |
.content .katex { font-size: 1.06em; }
|
|
|
|
| 206 |
background: var(--field); z-index: 1;
|
| 207 |
margin: -.8rem -1.1rem .4rem; padding: .8rem 1.1rem .4rem; }
|
| 208 |
|
| 209 |
+
/* key hints are noise on touch screens (either signal suffices) —
|
| 210 |
+
must match #controls kbd's specificity, which otherwise wins */
|
| 211 |
+
@media (hover: none), (pointer: coarse) {
|
| 212 |
+
kbd, #controls kbd { display: none; }
|
| 213 |
+
}
|
| 214 |
|
| 215 |
/* KaTeX inherits our reading size; display math gets breathing room */
|
| 216 |
.content .katex { font-size: 1.06em; }
|