@nanocollective/roster 0.1.0-alpha.33 → 0.1.0-alpha.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanocollective/roster",
3
- "version": "0.1.0-alpha.33",
3
+ "version": "0.1.0-alpha.35",
4
4
  "description": "An agent-run org, powered by GitHub. Scaffold AI staff members whose brain is a repo.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,9 +57,18 @@
57
57
  /* A bottom margin here would collapse against the row's own 16px and change nothing, so the
58
58
  extra room is put on the row. General sibling, not adjacent: labels can sit between them. */
59
59
  .tconflict ~ .tacts{margin-top:24px}
60
- .tlabels{margin-top:10px}
61
- .cmt{border-top:1px solid var(--line);padding:16px 0}
62
- .cmt.first{border-top:0}
60
+ .tmetalabels{display:inline-flex;gap:6px;margin-left:10px;vertical-align:middle;font-family:var(--sans)}
61
+ /* A comment is a card with a header strip, so each message has a clear start and end. */
62
+ .cmt{border:1px solid var(--line);border-radius:var(--r-lg);margin:14px 0;background:var(--panel);overflow:hidden}
63
+ .cmt.human{border-color:color-mix(in srgb,var(--accent) 40%,var(--line))}
64
+ .chead{display:flex;align-items:center;gap:8px;padding:9px 14px;background:var(--panel-2);
65
+ border-bottom:1px solid var(--line);font:13px var(--sans);color:var(--ink-dim)}
66
+ .cmt.human .chead{background:color-mix(in srgb,var(--accent) 8%,var(--panel-2))}
67
+ .chead .ic{--ic:14px;color:var(--ink-faint)}
68
+ .chead b{color:var(--ink);font-weight:600}
69
+ .chead .clogin{font:11.5px var(--mono);color:var(--ink-faint)}
70
+ .chead .cwhen{margin-left:auto;font-size:12px;color:var(--ink-faint);white-space:nowrap}
71
+ .cbodywrap{padding:12px 16px 14px}
63
72
  /* The header already draws a line under itself. Whatever the reversed thread puts first must
64
73
  not draw a second one right under it. */
65
74
  .lead{border-top:0}
@@ -138,7 +147,8 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
138
147
  /* ---------- timeline events ----------
139
148
  Not comments, so not comment-shaped: a marker, a line of prose, and whatever it points at.
140
149
  These carry the half of a thread that is agents referring to each other's work. */
141
- .tev{display:flex;gap:10px;align-items:flex-start;padding:9px 0 9px 2px;
150
+ .tev{display:flex;gap:10px;align-items:flex-start;padding:6px 0 6px 12px;margin-left:18px;
151
+ border-left:2px solid var(--line);
142
152
  font:12.5px/1.5 var(--sans);color:var(--ink-dim)}
143
153
  .tev .tico{flex:none;display:inline-flex;align-items:center;justify-content:center;
144
154
  width:16px;height:19px;color:var(--ink-faint);--ic:14px}
@@ -3,7 +3,7 @@
3
3
  import { getLabels, getPr, getThread, post, upload } from "../api.js";
4
4
  import { askText, askYes } from "../dialog.js";
5
5
  import { ago, el, esc, markCurrent, skeleton } from "../dom.js";
6
- import { icon, iconHTML } from "../icons.js";
6
+ import { icon, iconHTML, staffIcon } from "../icons.js";
7
7
  import { mdlite } from "../md.js";
8
8
  import { attachMentions } from "../mention.js";
9
9
  import { ensureInbox, refreshAll, stampCounts } from "../refresh.js";
@@ -365,17 +365,20 @@ function inboxScreen(m, opts) {
365
365
  head.innerHTML =
366
366
  '<div class="meta">' + esc(item.repo) + " · " + (item.kind === "pr" ? "PR " : "") + "#" + item.number +
367
367
  ' · <span class="' + (item.state === "OPEN" ? "ok" : "") + '">' + esc(item.state) + "</span>" +
368
- " · " + esc(item.author) + " · " + ago(item.updatedAt) + "</div>" +
368
+ " · " + esc(item.author) + " · " + ago(item.updatedAt) +
369
+ // The labels sit on this line, after the time, rather than taking a line of their own.
370
+ (item.labels.length
371
+ ? '<span class="tmetalabels">' +
372
+ item.labels.map((l) => '<span class="chip ' + tone(l) + '">' + esc(l) + "</span>").join("") +
373
+ "</span>"
374
+ : "") +
375
+ "</div>" +
369
376
  "<h3>" + esc(item.title) + "</h3>" +
370
377
  (item.mergeable === "CONFLICTING"
371
378
  ? '<p class="tconflict">This branch conflicts with its base and cannot be merged ' +
372
379
  "until someone rebases or merges the base into it.</p>"
373
380
  : "") +
374
- (item.labels.length
375
- ? '<div class="row tlabels">' +
376
- item.labels.map((l) => '<span class="chip ' + tone(l) + '">' + esc(l) + "</span>").join("") +
377
- "</div>"
378
- : "");
381
+ "";
379
382
  head.append(threadActions(item));
380
383
  delete viewer.dataset.wait;
381
384
  viewer.replaceChildren(head);
@@ -1047,15 +1050,36 @@ function xref(source, item, openThread) {
1047
1050
  return b;
1048
1051
  }
1049
1052
 
1053
+ /* Who wrote something, as a person would say it: a staff member by their role, with their
1054
+ icon; one of the humans by name; anyone else by their login. */
1055
+ function whoWrote(login) {
1056
+ const s = (S.data?.staff ?? []).find((x) => (x.soloBots ?? []).includes(login));
1057
+ if (s) return { name: s.name, glyph: staffIcon(s), kind: "staff" };
1058
+ if (isHuman(login)) {
1059
+ const h = humansOf().find((x) => x.github === login);
1060
+ return { name: h?.name ?? login, glyph: "person", kind: "human" };
1061
+ }
1062
+ return { name: login || "?", glyph: "person", kind: "other" };
1063
+ }
1064
+
1065
+ /* A comment is a card: a header strip saying who and when, then what they said. As text
1066
+ between thin rules, it was hard to see where one message ended and the next began. */
1050
1067
  function comment({ author, when, body, first, repo, badge, reactions }) {
1051
- const d = el("div", { className: "cmt" + (first ? " first" : "") });
1052
- const meta = el("div", { className: "meta" });
1053
- meta.append((author || "?") + " · " + new Date(when).toLocaleString());
1054
- if (badge) meta.append(el("span", { className: "pill", style: "margin-left:8px", textContent: badge }));
1055
- d.append(meta);
1056
- d.append(el("div", { className: "md cbody", innerHTML: mdlite(body || "_no description_", { repo }) }));
1068
+ const who = whoWrote(author);
1069
+ const d = el("div", { className: "cmt " + who.kind + (first ? " first" : "") });
1070
+ const head = el("div", { className: "chead" });
1071
+ head.append(icon(who.glyph, "ic"), el("b", { textContent: who.name }));
1072
+ if (who.name !== author && author) head.append(el("span", { className: "clogin", textContent: author }));
1073
+ if (badge) head.append(el("span", { className: "pill", textContent: badge }));
1074
+ head.append(
1075
+ el("span", { className: "cwhen", title: new Date(when).toLocaleString(), textContent: ago(when) }),
1076
+ );
1077
+ d.append(head);
1078
+ const bodyEl = el("div", { className: "cbodywrap" });
1079
+ bodyEl.append(el("div", { className: "md cbody", innerHTML: mdlite(body || "_no description_", { repo }) }));
1057
1080
  const marks = reactionRow(reactions);
1058
- if (marks) d.append(marks);
1081
+ if (marks) bodyEl.append(marks);
1082
+ d.append(bodyEl);
1059
1083
  return d;
1060
1084
  }
1061
1085