abullard1 commited on
Commit
60cdced
·
verified ·
1 Parent(s): 30588fc

Deploy conversion @ 960f4e5-space

Browse files
Files changed (4) hide show
  1. app.js +29 -4
  2. config.js +1 -1
  3. index.html +6 -6
  4. style.css +5 -2
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=2ce224c-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,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
- if (CONFIG.fields.every((field) => selection[field.name])) move(1);
 
 
 
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) { event.preventDefault(); controls._choose?.(binding[0], binding[1]); }
 
 
 
 
 
 
 
 
 
 
 
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: "2ce224c-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, "
 
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=2ce224c-space">
9
- <link rel="stylesheet" href="katex/katex.min.css?v=2ce224c-space">
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=2ce224c-space"></script>
26
- <script src="katex/auto-render.min.js?v=2ce224c-space"></script>
27
- <script src="config.js?v=2ce224c-space"></script>
28
- <script type="module" src="app.js?v=2ce224c-space"></script>
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
- @media (hover: none), (pointer: coarse) { kbd { display: none; } }
 
 
 
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; }