proctolog 0.1.0 → 0.2.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -3
  3. data/README.md +15 -4
  4. data/app/controllers/proctolog/application_controller.rb +1 -1
  5. data/app/controllers/proctolog/bursts_controller.rb +1 -1
  6. data/app/controllers/proctolog/choices_controller.rb +3 -1
  7. data/app/controllers/proctolog/decisions_controller.rb +1 -1
  8. data/app/controllers/proctolog/notes_controller.rb +2 -2
  9. data/app/controllers/proctolog/prompts_controller.rb +1 -1
  10. data/app/controllers/proctolog/pull_requests_controller.rb +2 -4
  11. data/app/controllers/proctolog/review_data_controller.rb +3 -1
  12. data/app/controllers/proctolog/shares_controller.rb +27 -0
  13. data/app/helpers/proctolog/decisions_helper.rb +6 -2
  14. data/app/helpers/proctolog/desk_helper.rb +48 -8
  15. data/app/helpers/proctolog/rounds_helper.rb +4 -2
  16. data/app/helpers/proctolog/shares_helper.rb +11 -0
  17. data/app/helpers/proctolog/standings_helper.rb +5 -4
  18. data/app/models/proctolog/burst.rb +17 -8
  19. data/app/models/proctolog/choices.rb +8 -0
  20. data/app/models/proctolog/doctor.rb +9 -1
  21. data/app/models/proctolog/github.rb +1 -1
  22. data/app/models/proctolog/marks.rb +1 -1
  23. data/app/models/proctolog/note.rb +4 -0
  24. data/app/models/proctolog/owner.rb +3 -0
  25. data/app/models/proctolog/pick.rb +12 -0
  26. data/app/models/proctolog/rounds.rb +37 -0
  27. data/app/models/proctolog/share/ngrok.rb +120 -0
  28. data/app/models/proctolog/share/settings.rb +23 -0
  29. data/app/models/proctolog/share/tailscale.rb +103 -0
  30. data/app/models/proctolog/share.rb +101 -0
  31. data/app/models/proctolog/standing.rb +24 -7
  32. data/app/views/proctolog/bursts/_line.html.erb +2 -2
  33. data/app/views/proctolog/bursts/_side.html.erb +6 -5
  34. data/app/views/proctolog/bursts/_side_stop.html.erb +4 -4
  35. data/app/views/proctolog/notes/_card.html.erb +7 -3
  36. data/app/views/proctolog/open_pull_requests/_side.html.erb +4 -3
  37. data/app/views/proctolog/open_pull_requests/_start.html.erb +8 -0
  38. data/app/views/proctolog/open_pull_requests/index.html.erb +4 -5
  39. data/app/views/proctolog/open_pull_requests/start/_first_time.html.erb +3 -0
  40. data/app/views/proctolog/open_pull_requests/start/_nothing_open.html.erb +3 -0
  41. data/app/views/proctolog/open_pull_requests/start/_nothing_written.html.erb +3 -0
  42. data/app/views/proctolog/shares/_line.html.erb +5 -0
  43. data/app/views/proctolog/shares/show.html.erb +48 -0
  44. data/app/views/proctolog/shares/ways/_ngrok.html.erb +6 -0
  45. data/app/views/proctolog/shares/ways/_tailscale.html.erb +2 -0
  46. data/bin/proctolog +92 -6
  47. data/completions/_proctolog +28 -4
  48. data/config/application.rb +23 -4
  49. data/config/initializers/grouse.rb +1 -1
  50. data/config/routes.rb +3 -0
  51. data/docs/manual/bursts.md +7 -2
  52. data/docs/manual/deciding.md +4 -1
  53. data/docs/manual/handover.md +6 -0
  54. data/docs/manual/reading.md +54 -27
  55. data/docs/manual/sharing.md +77 -0
  56. data/lib/proctolog/changelog.rb +50 -0
  57. data/lib/proctolog/version.rb +1 -1
  58. data/public/asking.js +31 -4
  59. data/public/decisions.js +4 -4
  60. data/public/desk.js +10 -6
  61. data/public/keys.js +6 -0
  62. data/public/notes.js +164 -58
  63. data/public/pick.css +21 -0
  64. data/public/respond.css +6 -0
  65. data/public/respond.js +51 -7
  66. data/public/review-drafts.js +47 -9
  67. data/public/review-rounds.js +27 -3
  68. data/public/share.css +58 -0
  69. data/public/side.js +11 -20
  70. data/public/terminal.css +451 -77
  71. data/public/viewer.js +700 -173
  72. metadata +18 -1
@@ -1,10 +1,11 @@
1
- import { helping, keyOf } from "./keys.js";
1
+ import { helping, keyOf, overThePage } from "./keys.js";
2
2
  import { prNumber } from "./pr-number.js";
3
3
  import {
4
4
  announce,
5
5
  handOver,
6
6
  draftsState,
7
7
  registerRounds,
8
+ settled,
8
9
  stale,
9
10
  updateDrafts,
10
11
  } from "./review-drafts.js";
@@ -28,6 +29,25 @@ const reviewData = async () => {
28
29
  if (!response.ok) throw new Error(`Desk returned ${response.status}`);
29
30
  return response.json();
30
31
  };
32
+ // The pane's ↩, on each pull request something came back on, in the words
33
+ // the desk gives (DeskHelper#side_mark).
34
+ const markThePane = (back = {}) => {
35
+ for (const row of document.querySelectorAll("[data-side-pr]")) {
36
+ const said = back[row.dataset.sidePr];
37
+ Object.assign(row.querySelector("[data-side-mark]"), {
38
+ textContent: said ? "↩" : "",
39
+ role: said ? "img" : null,
40
+ ariaLabel: said ?? null,
41
+ });
42
+ }
43
+ };
44
+ // Once the desk has the owner's word on an answer, it has moved the notes the
45
+ // answer was to (Rounds#move_notes), and the pane is marked again from it. A
46
+ // desk that does not answer leaves the pane as the page drew it.
47
+ const followThePane = async () => {
48
+ await settled();
49
+ markThePane((await reviewData()).back);
50
+ };
31
51
  // What GitHub draws of each piece of markdown, by its words, asked once while
32
52
  // the page is open: returned work is laid out again at every change of the
33
53
  // drafts, and must not fall back to markdown each time. A piece that cannot
@@ -589,6 +609,7 @@ const mark = (item, resolution, latest, kind, text) => {
589
609
  },
590
610
  };
591
611
  });
612
+ followThePane().catch(() => {});
592
613
  selectResponse(
593
614
  visibleResponses().find((node) => node.dataset.request === next) ||
594
615
  visibleResponses()[0],
@@ -1275,6 +1296,7 @@ registerRounds({
1275
1296
  context = await reviewData();
1276
1297
  importServerBatches();
1277
1298
  importServerResponses();
1299
+ markThePane(context.back);
1278
1300
  },
1279
1301
  });
1280
1302
 
@@ -1290,6 +1312,7 @@ tools
1290
1312
  ? "Agent response imported. Confirm or reply to its existing requests."
1291
1313
  : "The desk has this answer already.",
1292
1314
  );
1315
+ followThePane().catch(() => {});
1293
1316
  } catch (error) {
1294
1317
  announce(`Response could not be imported: ${error.message}`);
1295
1318
  } finally {
@@ -1361,8 +1384,9 @@ document
1361
1384
  .querySelector("[data-enter-responses]")
1362
1385
  ?.addEventListener("click", enterResponses);
1363
1386
  document.addEventListener("focusin", (event) => {
1364
- // A picture looked at closely leaves the response marked: Esc comes back to it.
1365
- if (event.target.closest("dialog")) return;
1387
+ // A picture looked at closely, or a note written in Grouse's overlay, leaves
1388
+ // the response marked: Esc comes back to it.
1389
+ if (overThePage(event.target)) return;
1366
1390
  const node = event.target.closest("[data-request]");
1367
1391
  if (node) markResponse(node);
1368
1392
  else activeResponse?.removeAttribute("aria-current");
data/public/share.css ADDED
@@ -0,0 +1,58 @@
1
+ /* Sharing the desk: where it is now, then each way to share it, in the
2
+ route's own frame, with what it needs first or its button under its words. */
3
+
4
+ .share .now {
5
+ margin-block: 0 0.5lh;
6
+ }
7
+
8
+ .share .ways {
9
+ border: 1px solid var(--rule);
10
+ border-top: 0;
11
+ background: var(--surface);
12
+ }
13
+
14
+ .share .way {
15
+ display: grid;
16
+ row-gap: 8px;
17
+ padding: var(--row-pad) 12px;
18
+ border-bottom: 1px solid var(--rule-soft);
19
+ }
20
+
21
+ .share .way:last-child {
22
+ border-bottom: 0;
23
+ }
24
+
25
+ .share .way h3 {
26
+ margin: 0;
27
+ font-size: 1rem;
28
+ color: var(--ink);
29
+ }
30
+
31
+ .share .way p {
32
+ margin: 0;
33
+ }
34
+
35
+ .share .way .facts {
36
+ color: var(--quiet);
37
+ }
38
+
39
+ .share .way form {
40
+ display: grid;
41
+ row-gap: 8px;
42
+ justify-items: start;
43
+ }
44
+
45
+ .share .allow {
46
+ display: flex;
47
+ flex-wrap: wrap;
48
+ gap: 4px 2ch;
49
+ align-items: baseline;
50
+ }
51
+
52
+ .share .allow input {
53
+ width: min(40ch, 100%);
54
+ }
55
+
56
+ .share code {
57
+ white-space: nowrap;
58
+ }
data/public/side.js CHANGED
@@ -14,32 +14,23 @@ const made = (name, properties = {}, ...children) => {
14
14
  return element;
15
15
  };
16
16
 
17
- // "3 of 19 decisions reviewed", from the row's "~" and "3/19".
18
- const progressInWords = (mark, fraction) => {
19
- if (!fraction) return "no decisions to review";
20
- const [reviewed, total] = fraction.split("/").map(Number);
21
- const decisions = total === 1 ? "decision" : "decisions";
22
- if (mark === "x")
23
- return total === 1
24
- ? "its one decision reviewed"
25
- : `all ${total} decisions reviewed`;
26
- if (!reviewed) return `none of ${total} ${decisions} reviewed yet`;
27
- return `${reviewed} of ${total} ${decisions} reviewed`;
28
- };
29
-
30
17
  const MOVED = {
31
18
  changed: "changed on GitHub since it was read",
32
19
  closed: "no longer open on GitHub",
33
20
  };
34
21
 
22
+ // The row's marks said in words: what came back from the agent, then each
23
+ // grade's beads with what they count, in the words the server labelled them with.
35
24
  const marksOf = (row) => {
36
- const mark = row.querySelector("[data-side-mark]").textContent.trim();
37
- const fraction = row.querySelector("[data-side-fraction]").textContent.trim();
25
+ const back = row.querySelector("[data-side-mark]");
26
+ const grades = row.querySelectorAll("[data-side-decisions] [role=img]");
38
27
  return [
39
- [
40
- [mark, fraction].filter(Boolean).join(" "),
41
- progressInWords(mark, fraction),
42
- ],
28
+ back.textContent && [back.textContent, back.ariaLabel],
29
+ ...[...grades].map((grade) => [
30
+ made("span", { className: "beads" }, grade.cloneNode(true)),
31
+ grade.ariaLabel,
32
+ ]),
33
+ !grades.length && ["", "no decisions to review"],
43
34
  row.dataset.moved && ["*", MOVED[row.dataset.moved]],
44
35
  ].filter(Boolean);
45
36
  };
@@ -62,7 +53,7 @@ const cardOf = (row) => {
62
53
  made(
63
54
  "div",
64
55
  {},
65
- made("dt", { textContent: shown }),
56
+ made("dt", {}, shown),
66
57
  made("dd", { textContent: said }),
67
58
  ),
68
59
  ),