@nanocollective/roster 0.1.0-alpha.37 → 0.1.0-alpha.39

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.37",
3
+ "version": "0.1.0-alpha.39",
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": {
@@ -194,3 +194,6 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
194
194
  /* Unread, from GitHub's notifications: a bar on the left and a heavier title. Cleared on open. */
195
195
  .irow.unread{box-shadow:inset 3px 0 0 var(--blue);background:color-mix(in srgb,var(--blue) 6%,transparent)}
196
196
  .irow.unread .ititle{font-weight:700;color:var(--ink)}
197
+
198
+ /* A little room between rows, so unread cards and the selected row do not run together. */
199
+ .irow{margin:3px 0}
@@ -78,13 +78,15 @@
78
78
  .sidefoot .nav:hover{color:var(--ink-dim)}
79
79
 
80
80
  /* ---------- main ---------- */
81
- main{padding:36px 44px 96px;max-width:1220px;overflow-x:hidden}
81
+ /* clip, not hidden: hidden makes main a scroll container, and nothing inside it can be sticky. */
82
+ main{padding:36px 44px 96px;max-width:1220px;overflow-x:clip}
82
83
 
83
84
  /* ---------- tree beside viewer ---------- */
84
85
  .split{display:grid;grid-template-columns:314px 1fr;gap:16px;align-items:start}
85
86
  @media (max-width:900px){.split{grid-template-columns:1fr}}
87
+ /* The list beside a viewer stays in view while a long file or thread scrolls. */
86
88
  .tree{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:6px;
87
- max-height:74vh;overflow:auto;box-shadow:var(--shadow-sm)}
89
+ max-height:calc(100vh - 40px);overflow:auto;box-shadow:var(--shadow-sm);position:sticky;top:20px}
88
90
  .tdir{font:600 11px var(--mono);color:var(--ink-faint);padding:9px 8px 4px;letter-spacing:.02em}
89
91
  .tdir.first{padding-top:3px}
90
92
  .tfile{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;background:none;
@@ -326,7 +326,6 @@ function paintInboxLinks() {
326
326
  // Inbox itself is everything. Under it: each staff member, then the product repos' issues.
327
327
  const links = [
328
328
  ["", "All", "inbox"],
329
- ["@unread", "Unread", "dot"],
330
329
  ...S.data.staff.map((s) => [s.handle, s.name, staffIcon(s)]),
331
330
  ["@issues", "Issues", "issue-open"],
332
331
  ];
@@ -686,7 +686,20 @@ function inboxScreen(m, opts) {
686
686
  body: body || undefined,
687
687
  });
688
688
  DRAFTS.delete(key);
689
- await refreshAll(false);
689
+ /* What changed is known, so it is changed here rather than re-reading every repo,
690
+ which redrew the whole inbox for one issue. The next refresh confirms it. */
691
+ const me = humansOf()[0]?.github ?? "you";
692
+ const now = new Date().toISOString();
693
+ item.events = [
694
+ ...(item.events ?? []),
695
+ ...(body ? [{ type: "comment", actor: me, createdAt: now, body }] : []),
696
+ { type: closing ? "closed" : "reopened", actor: me, createdAt: now },
697
+ ];
698
+ item.state = closing ? "CLOSED" : "OPEN";
699
+ item.updatedAt = now;
700
+ stampCounts();
701
+ paint();
702
+ openThread({ repo: item.repo, number: item.number, kind: item.kind });
690
703
  } catch (e) { failed(e); }
691
704
  };
692
705
  buttons.push(closeBtn);
@@ -714,7 +727,12 @@ function inboxScreen(m, opts) {
714
727
  busy(true, "merging…");
715
728
  try {
716
729
  await post({ action: "merge", repo: item.repo, number: item.number });
717
- await refreshAll(false);
730
+ // As with closing: the change is known, so it is made here rather than re-reading every repo.
731
+ item.state = "MERGED";
732
+ item.updatedAt = new Date().toISOString();
733
+ stampCounts();
734
+ paint();
735
+ openThread({ repo: item.repo, number: item.number, kind: item.kind });
718
736
  } catch (e) { failed(e); }
719
737
  };
720
738
  buttons.push(merge);
@@ -780,6 +798,19 @@ function inboxScreen(m, opts) {
780
798
  * you are still typing and the button has to stay as it was; once it closes there is a request
781
799
  * in flight against GitHub that can take seconds, and that is what needs saying.
782
800
  */
801
+ /**
802
+ * Who a reply is for, by default: the staff member whose tracker the issue is on, or whose App
803
+ * opened it. They are only woken by an @mention, so a reply without one reached nobody.
804
+ */
805
+ export function addressee(item) {
806
+ const staff = S.data?.staff ?? [];
807
+ const s =
808
+ staff.find((x) => x.brain === item.repo) ??
809
+ staff.find((x) => (x.soloBots ?? []).includes(item.author));
810
+ if (!s) return "";
811
+ return (s.mention || "@" + s.handle) + " ";
812
+ }
813
+
783
814
  async function openReply(item, anchor, onSend) {
784
815
  const key = item.repo + "#" + item.number + (anchor ? " · " + anchor.path : "");
785
816
  let deaf = null;
@@ -788,7 +819,7 @@ async function openReply(item, anchor, onSend) {
788
819
  title: "Reply to " + item.repo + " #" + item.number,
789
820
  hint: anchor ? anchor.path : item.title,
790
821
  confirm: "Comment",
791
- value: DRAFTS.get(key) ?? "",
822
+ value: DRAFTS.get(key) ?? addressee(item),
792
823
  /* It goes out through whoever's `gh` is signed in here, which is only knowably one person
793
824
  when the org has one. With two it would be a guess, and a wrong name on a reply box is
794
825
  worse than no name. */
@@ -799,6 +830,10 @@ async function openReply(item, anchor, onSend) {
799
830
  else DRAFTS.delete(key);
800
831
  };
801
832
  deaf = deafNote(ta, item, anchor);
833
+ // Typing starts after the @mention, not in front of it.
834
+ setTimeout(() => {
835
+ ta.selectionStart = ta.selectionEnd = ta.value.length;
836
+ }, 0);
802
837
  return el("div", {}, [attachBox(ta, () => item.repo).node, deaf.node]);
803
838
  },
804
839
  });