@nanocollective/roster 0.1.0-alpha.4 → 0.1.0-alpha.6

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.
@@ -5,15 +5,16 @@
5
5
  the inbox hard to scan. The line is dropped either side of the selected row, whose own
6
6
  background is already doing the separating. */
7
7
  .irow{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
8
- padding:11px 10px;border-radius:7px;color:var(--ink-dim);
9
- border-top:1px solid transparent}
8
+ padding:11px 12px;border-radius:var(--r);color:var(--ink-dim);
9
+ border-top:1px solid transparent;transition:background .12s var(--ease)}
10
10
  .irow + .irow{border-top-color:var(--line)}
11
11
  .irow[aria-current="true"],.irow[aria-current="true"] + .irow{border-top-color:transparent}
12
- .irow:hover{background:var(--panel-2)}
12
+ .irow:hover{background:var(--hover)}
13
+ .irow:hover + .irow,.irow:hover{border-top-color:transparent}
13
14
  .irow:focus{outline:none}
14
- .irow:focus-visible{outline:2px solid var(--accent-dim);outline-offset:-2px}
15
- .irow[aria-current="true"]{background:var(--panel-2);box-shadow:inset 2px 0 0 var(--accent)}
16
- .ititle{font:13.5px/1.4 var(--sans);color:var(--ink);display:-webkit-box;-webkit-line-clamp:2;
15
+ .irow:focus-visible{box-shadow:inset 0 0 0 1px var(--accent-dim)}
16
+ .irow[aria-current="true"]{background:var(--accent-soft)}
17
+ .ititle{font:500 13.5px/1.4 var(--sans);letter-spacing:-.005em;color:var(--ink);display:-webkit-box;-webkit-line-clamp:2;
17
18
  -webkit-box-orient:vertical;overflow:hidden}
18
19
  .irow[aria-current="true"] .ititle{color:var(--accent)}
19
20
  /* The rest of the row follows the title. The project label kept the flat grey it has when
@@ -25,7 +26,7 @@
25
26
  .dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--warn);
26
27
  margin-right:6px;vertical-align:2px}
27
28
  .imeta{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:5px}
28
- .imeta .repo{font:10px var(--sans);letter-spacing:.03em;text-transform:uppercase;
29
+ .imeta .repo{font:600 10px var(--sans);letter-spacing:.06em;text-transform:uppercase;
29
30
  color:var(--ink-faint);border-right:1px solid var(--line);padding-right:7px}
30
31
  .imeta .num{font:10.5px var(--mono);color:var(--ink-faint)}
31
32
  .imeta .when{font:10.5px var(--sans);color:var(--ink-faint);margin-left:auto}
@@ -40,10 +41,22 @@
40
41
  .ist{display:inline-flex;--ic:12px}
41
42
  .ist.closed{color:var(--derived)} .ist.merged{color:var(--derived)}
42
43
  .ck.passing{color:var(--accent)} .ck.failing{color:var(--err)} .ck.pending{color:var(--warn)}
44
+ /* Filled rather than outlined like the label chips beside it. A branch that no longer applies
45
+ to its base is the one state where a row can show green checks and still not be mergeable,
46
+ so it has to win the row it is in. */
47
+ .chip.conflict{color:var(--err);border-color:color-mix(in srgb,var(--err) 50%,transparent);
48
+ background:color-mix(in srgb,var(--err) 14%,transparent)}
43
49
 
44
50
  /* ---------- thread ---------- */
45
51
  .thead{border-bottom:1px solid var(--line);padding-bottom:16px}
46
- .thead h3{margin:8px 0 0;font:600 17px/1.35 var(--sans);letter-spacing:-.012em;color:var(--ink)}
52
+ .thead h3{margin:8px 0 0;font:600 19px/1.3 var(--sans);letter-spacing:-.02em;color:var(--ink)}
53
+ /* The same fact as the chip, said in full, once, at the top of the thread it applies to.
54
+ Tight to the title and a clear gap above the buttons: it is a statement about this pull
55
+ request, not a caption on the controls. At an even 10/16 it read as the latter. */
56
+ .tconflict{margin:4px 0 0;font:12.5px/1.5 var(--sans);color:var(--err)}
57
+ /* A bottom margin here would collapse against the row's own 16px and change nothing, so the
58
+ extra room is put on the row. General sibling, not adjacent: labels can sit between them. */
59
+ .tconflict ~ .tacts{margin-top:24px}
47
60
  .tlabels{margin-top:10px}
48
61
  .cmt{border-top:1px solid var(--line);padding:16px 0}
49
62
  .cmt.first{border-top:0}
@@ -53,7 +66,7 @@
53
66
  /* Reactions. Small, under the comment they belong to, the way GitHub puts them — an agent's
54
67
  👀 is an acknowledgement and it should be readable at a glance rather than counted. */
55
68
  .reacts{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
56
- .react{font:11px var(--sans);border:1px solid var(--line);border-radius:20px;padding:2px 9px;
69
+ .react{font:11px var(--sans);border:1px solid var(--line);border-radius:999px;padding:2px 9px;
57
70
  color:var(--ink-dim);background:var(--panel-2);cursor:default}
58
71
  /* Reply, close and merge, beside the title rather than under the last comment. A thread is
59
72
  read newest-first and answered from the top, so nothing here is worth scrolling to. */
@@ -67,10 +80,11 @@
67
80
  rather than three places to navigate to. */
68
81
  .tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin:2px 0 0}
69
82
  .tab{background:none;border:0;border-bottom:2px solid transparent;cursor:pointer;
70
- font:12.5px var(--sans);color:var(--ink-faint);padding:9px 12px;margin-bottom:-1px}
83
+ font:500 12.5px var(--sans);color:var(--ink-faint);padding:9px 12px;margin-bottom:-1px;
84
+ transition:color .15s var(--ease),border-color .15s var(--ease)}
71
85
  .tab:hover{color:var(--ink)}
72
86
  .tab[aria-current="true"]{color:var(--ink);border-bottom-color:var(--accent)}
73
- .tab:focus-visible{outline:2px solid var(--accent-dim);outline-offset:-2px}
87
+ .tab:focus-visible{box-shadow:inset 0 0 0 1px var(--accent-dim);border-radius:6px 6px 0 0}
74
88
 
75
89
  .prcommit{display:flex;gap:10px;align-items:baseline;padding:8px 0;
76
90
  border-top:1px solid var(--line);font-size:12.5px}
@@ -89,9 +103,9 @@
89
103
  .labelpick .chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
90
104
  .chip.pick{background:none;cursor:pointer;padding:3px 9px;font-size:11px;color:var(--ink-dim)}
91
105
  .chip.pick:hover{border-color:var(--ink-faint);color:var(--ink)}
92
- .chip.pick:focus-visible{outline:2px solid var(--accent-dim);outline-offset:1px}
93
- .chip.pick[aria-pressed="true"]{background:color-mix(in srgb,var(--accent) 16%,transparent);
94
- border-color:var(--accent-dim);color:var(--ink)}
106
+ .chip.pick:focus-visible{box-shadow:0 0 0 3px var(--accent-soft);border-color:var(--accent)}
107
+ .chip.pick[aria-pressed="true"]{background:var(--accent-soft);
108
+ border-color:var(--accent-dim);color:var(--accent)}
95
109
 
96
110
  /* ---------- the @ list ----------
97
111
  A mention is the mechanism: a staff member's workflow gates on their handle, so a misspelled
@@ -101,12 +115,12 @@
101
115
  The height is capped well under a dialog's, because it is placed inside one and a dialog
102
116
  scrolls its own box: anything taller is not long, it is cut off. */
103
117
  .mentions{position:absolute;z-index:30;min-width:280px;max-width:min(420px,90%);max-height:196px;
104
- overflow-y:auto;background:var(--panel);border:1px solid var(--line);border-radius:8px;
105
- padding:4px;box-shadow:0 14px 34px rgba(0,0,0,.28)}
118
+ overflow-y:auto;background:var(--bg-elev);border:1px solid var(--border-strong);border-radius:var(--r);
119
+ padding:4px;box-shadow:var(--shadow-lg)}
106
120
  .mrow{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;width:100%;text-align:left;
107
121
  background:none;border:0;border-radius:6px;padding:6px 8px;cursor:pointer;font:inherit}
108
- .mrow:hover{background:var(--panel-2)}
109
- .mrow[aria-selected="true"]{background:var(--panel-2);box-shadow:inset 2px 0 0 var(--accent)}
122
+ .mrow:hover{background:var(--hover)}
123
+ .mrow[aria-selected="true"]{background:var(--accent-soft)}
110
124
  .mrow .mat{font:12px var(--mono);color:var(--accent)}
111
125
  .mrow .mname{font:12.5px var(--sans);color:var(--ink);overflow:hidden;text-overflow:ellipsis;
112
126
  white-space:nowrap}
@@ -144,9 +158,10 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
144
158
  /* An item this thread points at, as a row you can open here rather than a link that throws
145
159
  you out to GitHub. Open and closed read at a glance, which is the whole reason to show it. */
146
160
  .xref{display:flex;gap:8px;align-items:baseline;width:100%;text-align:left;background:none;
147
- border:1px solid var(--line);border-radius:7px;padding:6px 10px;margin-top:5px;
148
- cursor:pointer;color:var(--ink-dim);font:12.5px var(--sans)}
149
- .xref:hover{border-color:var(--accent-dim);background:var(--panel-2);color:var(--ink)}
161
+ border:1px solid var(--line);border-radius:var(--r);padding:7px 11px;margin-top:6px;
162
+ cursor:pointer;color:var(--ink-dim);font:12.5px var(--sans);background:var(--bg-elev);
163
+ transition:border-color .15s var(--ease),background .15s var(--ease),color .15s var(--ease)}
164
+ .xref:hover{border-color:var(--border-strong);background:var(--surface-2);color:var(--ink)}
150
165
  .xref .st{flex:none;display:inline-flex;--ic:14px;position:relative;top:2px}
151
166
  .xref .st.open{color:var(--accent)} .xref .st.closed{color:var(--derived)}
152
167
  .xref .st.merged{color:var(--derived)} .xref .st.draft{color:var(--ink-faint)}
@@ -1,75 +1,102 @@
1
1
  /* The page frame: sidebar, main column, and the tree-beside-viewer split every screen uses. */
2
2
 
3
- .app{display:grid;grid-template-columns:246px 1fr;min-height:100vh}
3
+ .app{display:grid;grid-template-columns:252px 1fr;min-height:100vh}
4
4
  /* ---------- sidebar ---------- */
5
- .side{background:var(--panel);border-right:1px solid var(--line);padding:20px 0 14px;
6
- position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column}
7
- .brand{padding:0 20px 18px;font:600 15px var(--sans);letter-spacing:-.01em}
5
+ .side{background:var(--side);border-right:1px solid var(--line);padding:16px 10px 12px;
6
+ position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column;
7
+ gap:1px}
8
+ .brand{padding:4px 10px 16px;font:600 15px/1.2 var(--sans);letter-spacing:-.02em;
9
+ display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center}
8
10
  /* The tab icon, in the corner it belongs in. Drawn rather than fetched so it takes the
9
11
  theme's accent and costs no request. */
10
- .brand .mark{display:inline-block;width:9px;height:9px;border-radius:50%;
11
- background:var(--accent);margin-right:7px;vertical-align:1px}
12
+ .brand .mark{grid-row:1/3;display:inline-block;width:24px;height:24px;border-radius:7px;
13
+ background:var(--accent-fill)}
12
14
  .brand small{display:block;color:var(--ink-faint);font-weight:400;font-size:11.5px;
13
- letter-spacing:.02em;margin-top:2px;margin-left:16px}
14
- .refreshall{display:flex;align-items:center;gap:8px;width:calc(100% - 24px);margin:0 12px 4px;
15
- background:var(--panel-2);border:1px solid var(--line);color:var(--ink-faint);
16
- border-radius:7px;padding:7px 10px;cursor:pointer;font:11px var(--sans);text-align:left}
17
- .refreshall:hover{color:var(--ink);border-color:var(--accent-dim)}
15
+ letter-spacing:0;margin-top:2px;overflow:hidden;text-overflow:ellipsis;
16
+ white-space:nowrap}
17
+ .refreshall{display:flex;align-items:center;gap:8px;width:100%;margin:0 0 12px;
18
+ background:var(--hover);border:1px solid transparent;color:var(--ink-dim);
19
+ border-radius:8px;padding:6px 10px;cursor:pointer;font:11.5px var(--sans);text-align:left;
20
+ transition:color .15s var(--ease),background .15s var(--ease)}
21
+ .refreshall:hover{color:var(--ink);background:var(--active)}
18
22
  .refreshall .sync{display:inline-flex;--ic:13px;flex:none}
19
23
  .refreshall.spin .sync{animation:spin .9s linear infinite}
20
24
 
21
- .sect{padding:14px 20px 6px;font:600 10.5px var(--sans);letter-spacing:.09em;
25
+ .sect{padding:16px 10px 6px;font:600 11px var(--sans);letter-spacing:.08em;
22
26
  text-transform:uppercase;color:var(--ink-faint)}
27
+ /* The Staff heading is also the fold for the list under it, so it is a button — and has to
28
+ go on reading as a heading rather than as a control. The caret and the left padding
29
+ are the staff rows' own, so the two carets line up in one column. */
30
+ .sectfold{display:flex;align-items:center;gap:7px;width:100%;background:none;border:0;
31
+ border-radius:6px;cursor:pointer;text-align:left;font:600 11px var(--sans);letter-spacing:.08em;
32
+ text-transform:uppercase;color:var(--ink-faint);margin-top:12px;
33
+ transition:color .15s var(--ease)}
34
+ .sectfold:hover{color:var(--ink-dim)}
35
+ .sectfold .caret{display:inline-flex;--ic:12px;flex:none;transition:transform .15s var(--ease)}
36
+ .sectfold[aria-expanded="false"] .caret{transform:rotate(-90deg)}
37
+ /* The count is a number, not a heading: mono, and none of the heading's letterspacing. */
38
+ .sectfold .n{font:10.5px var(--mono);letter-spacing:0;text-transform:none;color:var(--ink-faint);
39
+ font-variant-numeric:tabular-nums}
23
40
  /* Flex, not block with a float. The icons are block-level SVGs and the trailing count has to
24
41
  sit hard right; `float` does neither of those inside a flex row, and did neither of them
25
42
  outside one once the icons arrived. */
26
- .nav{display:flex;align-items:center;width:100%;text-align:left;background:none;border:0;
27
- color:var(--ink-dim);font:14px var(--sans);padding:7px 20px;cursor:pointer;
28
- border-left:2px solid transparent}
29
- .nav:hover{color:var(--ink);background:var(--panel-2)}
30
- .nav[aria-current="true"]{color:var(--ink);border-left-color:var(--accent);background:var(--panel-2)}
31
- .nav .ic{color:var(--ink-faint);margin-right:9px;--ic:15px;flex:none}
32
- .nav .n{margin-left:auto;padding-left:10px;color:var(--ink-faint);font:11px var(--mono)}
33
- .nav.top{margin-bottom:2px}
43
+ .nav{position:relative;display:flex;align-items:center;width:100%;text-align:left;background:none;
44
+ border:0;border-radius:7px;color:var(--ink-dim);font:500 13.5px var(--sans);padding:6px 10px;
45
+ cursor:pointer;min-height:32px;
46
+ transition:background .15s var(--ease),color .15s var(--ease)}
47
+ .nav:hover{color:var(--ink);background:var(--hover)}
48
+ .nav:focus-visible{box-shadow:0 0 0 2px var(--accent-soft),inset 0 0 0 1px var(--accent-dim)}
49
+ .nav[aria-current="true"]{color:var(--ink);background:var(--sel)}
50
+ .nav .ic{color:var(--ink-faint);margin-right:10px;--ic:15px;flex:none;
51
+ transition:color .15s var(--ease)}
52
+ .nav:hover .ic{color:var(--ink-dim)}
53
+ .nav[aria-current="true"] .ic{color:var(--accent)}
54
+ .nav .n{margin-left:auto;padding-left:10px;color:var(--ink-faint);font:500 10.5px var(--mono);
55
+ font-variant-numeric:tabular-nums}
56
+ .nav .n:not(:empty):not(.wait){padding:1px 7px;border-radius:999px;margin-left:auto;
57
+ background:var(--sel);color:var(--ink-dim);min-width:20px;
58
+ text-align:center}
59
+ .nav .n.wait{margin-left:auto}
60
+ .nav.top{margin-bottom:0}
34
61
 
35
62
  /* Staff are rows that unfold into their own views, rather than a dropdown plus a list you
36
63
  have to connect for yourself. The nesting is the hierarchy, said out loud. */
37
- .staffrow{display:flex;align-items:center;gap:7px;font:13.5px var(--sans);color:var(--ink);
38
- padding-right:14px}
39
- .staffrow .caret{color:var(--ink-faint);--ic:12px;flex:none;transition:transform .12s ease}
64
+ .staffrow{display:flex;align-items:center;gap:7px;font:500 13px var(--sans);color:var(--ink);
65
+ padding-right:10px}
66
+ .staffrow .caret{color:var(--ink-faint);--ic:12px;flex:none;transition:transform .15s var(--ease)}
40
67
  .staffrow[aria-expanded="false"] .caret{transform:rotate(-90deg)}
41
68
  .staffrow .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
42
69
  .staffrow .hh{font:10.5px var(--mono);color:var(--ink-faint);flex:none}
43
70
  .staffrow[aria-selected="true"]{color:var(--ink)}
44
- .staffviews{padding-bottom:4px}
45
- /* `subnav`, not `sub`: `.sub` is the page subtitle and carries a 24px bottom margin, which
71
+ .staffviews{position:relative;padding:2px 0 6px;margin-left:12px}
72
+ /* `subnav`, not `sub`: `.sub` is the page subtitle and carries a bottom margin, which
46
73
  these rows inherited as a gap you could park a car in. */
47
- .nav.subnav{padding:5px 20px 5px 40px;font-size:13px}
74
+ .nav.subnav{padding:4px 10px 4px 16px;font-size:12.5px;min-height:28px}
48
75
  .nav.subnav .n{font-size:10px}
49
- .sidefoot{margin-top:auto;padding-top:14px}
76
+ .sidefoot{margin-top:auto;padding-top:14px;border-top:1px solid var(--line)}
50
77
  .sidefoot .nav{font-size:12.5px;color:var(--ink-faint)}
51
78
  .sidefoot .nav:hover{color:var(--ink-dim)}
52
79
 
53
80
  /* ---------- main ---------- */
54
- main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
81
+ main{padding:36px 44px 96px;max-width:1220px;overflow-x:hidden}
55
82
 
56
83
  /* ---------- tree beside viewer ---------- */
57
84
  .split{display:grid;grid-template-columns:314px 1fr;gap:16px;align-items:start}
58
85
  @media (max-width:900px){.split{grid-template-columns:1fr}}
59
- .tree{background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:9px;
60
- max-height:74vh;overflow:auto}
61
- .tdir{font:600 11px var(--mono);color:var(--ink-faint);padding:8px 8px 4px;letter-spacing:.04em}
62
- .tdir.first{padding-top:2px}
86
+ .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)}
88
+ .tdir{font:600 11px var(--mono);color:var(--ink-faint);padding:9px 8px 4px;letter-spacing:.02em}
89
+ .tdir.first{padding-top:3px}
63
90
  .tfile{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;background:none;
64
- border:0;color:var(--ink-dim);font:12.5px var(--mono);padding:4px 8px;border-radius:5px;
65
- cursor:pointer}
66
- .tfile:hover{background:var(--panel-2);color:var(--ink)}
67
- .tfile[aria-current="true"]{background:var(--panel-2);color:var(--accent)}
91
+ border:0;color:var(--ink-dim);font:12.5px var(--mono);padding:5px 8px;border-radius:6px;
92
+ cursor:pointer;transition:background .12s var(--ease),color .12s var(--ease)}
93
+ .tfile:hover{background:var(--hover);color:var(--ink)}
94
+ .tfile[aria-current="true"]{background:var(--accent-soft);color:var(--accent)}
68
95
  .tfile span.t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
69
96
  .tfile i{color:var(--ink-faint);font-style:normal;font-size:10.5px;flex:none;
70
97
  font-variant-numeric:tabular-nums}
71
98
  .tfile:focus{outline:none}
72
- .tfile:focus-visible{outline:2px solid var(--accent-dim);outline-offset:-2px}
99
+ .tfile:focus-visible{box-shadow:inset 0 0 0 1px var(--accent-dim)}
73
100
 
74
101
  /* A search result: the page, and the lines the words were actually found in. Without the lines
75
102
  this is a filter on titles, which is the thing that was not good enough. */
@@ -82,8 +109,8 @@ main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
82
109
  .thit .hitline em{font-style:normal;color:var(--ink);background:color-mix(in srgb,var(--accent) 22%,transparent);
83
110
  border-radius:3px;padding:0 2px}
84
111
 
85
- .viewer{background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:20px 22px;
86
- min-height:340px;max-height:74vh;overflow:auto}
112
+ .viewer{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:22px 26px;
113
+ min-height:340px;max-height:74vh;overflow:auto;box-shadow:var(--shadow-sm)}
87
114
  .viewer pre{margin:0;white-space:pre-wrap;word-break:break-word;font:12.5px/1.65 var(--mono);color:var(--ink-dim)}
88
115
  /* Source keeps its own line breaks and scrolls, rather than wrapping into soup. */
89
116
  .viewer pre.code{white-space:pre;word-break:normal;overflow-x:auto}
@@ -94,12 +121,14 @@ main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
94
121
  .viewer audio.player{background:none}
95
122
  .viewer table{border-collapse:collapse;font:12px var(--mono);width:100%}
96
123
  .viewer td,.viewer th{border:1px solid var(--line);padding:4px 8px;text-align:left;white-space:nowrap}
97
- .viewer th{position:sticky;top:0;background:var(--panel-2);color:var(--ink)}
124
+ .viewer th{position:sticky;top:0;background:var(--panel-2);color:var(--ink);font-weight:600}
98
125
 
99
126
  .gallery{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
100
127
  .gallery figure{margin:0;position:relative}
101
128
  .gallery img,.gallery video{width:100%;aspect-ratio:4/3;object-fit:contain;background:var(--panel-2);
102
- border-radius:6px;border:1px solid var(--line);cursor:pointer;display:block}
129
+ border-radius:var(--r);border:1px solid var(--line);cursor:pointer;display:block;
130
+ transition:border-color .15s var(--ease),transform .15s var(--ease)}
131
+ .gallery img:hover,.gallery video:hover{border-color:var(--border-strong)}
103
132
  /* A still frame and a photograph look identical in a grid, so the video says which it is. */
104
133
  .playmark{position:absolute;top:8px;right:8px;display:inline-flex;padding:4px;border-radius:50%;
105
134
  background:color-mix(in srgb,#000 55%,transparent);color:#fff;--ic:12px;pointer-events:none}
@@ -1,34 +1,37 @@
1
1
  /* One renderer, two sizes: .cbody is a comment in a thread, .doc is a file being read.
2
2
  Everything shared lives on .md so a fix reaches both. */
3
3
 
4
- .md{line-height:1.65}
4
+ .md{line-height:1.7}
5
5
  .md>:first-child{margin-top:0}
6
6
  .md p{margin:0 0 10px}
7
7
  .md h1,.md h2,.md h3,.md h4,.md h5,.md h6{color:var(--ink);font-weight:600;line-height:1.3;
8
- margin:22px 0 8px;letter-spacing:-.012em}
9
- .md h1{font-size:20px} .md h2{font-size:17px} .md h3{font-size:15px}
8
+ margin:26px 0 10px;letter-spacing:-.02em}
9
+ .md h1{font-size:22px} .md h2{font-size:17.5px} .md h3{font-size:15px}
10
10
  .md h4,.md h5,.md h6{font-size:13.5px;color:var(--ink-dim)}
11
- .md h1,.md h2{padding-bottom:6px;border-bottom:1px solid var(--line)}
11
+ .md h1,.md h2{padding-bottom:8px;border-bottom:1px solid var(--line)}
12
12
  .md ul,.md ol{margin:0 0 10px;padding-left:20px}
13
13
  .md li{margin:3px 0}
14
14
  .md li>ul,.md li>ol{margin:4px 0 0}
15
15
  .md ul.tasks{list-style:none;padding-left:1px}
16
16
  .md .box{color:var(--ink-faint);margin-right:6px;display:inline-flex;--ic:14px;
17
17
  vertical-align:-3px}
18
- .md code{font:12px var(--mono);color:var(--accent);background:var(--panel-2);
19
- padding:1px 4px;border-radius:4px;word-break:break-word}
20
- .md pre{background:var(--panel-2);border:1px solid var(--line);border-radius:7px;
21
- padding:11px 13px;font:12px/1.62 var(--mono);margin:10px 0;color:var(--ink-dim);
18
+ .md code{font:12px var(--mono);color:var(--ink);background:color-mix(in srgb,var(--ink) 7%,transparent);
19
+ padding:1px 5px;border-radius:5px;word-break:break-word}
20
+ .md pre{background:var(--surface-2);border:0;border-radius:var(--r);
21
+ padding:13px 15px;font:12px/1.62 var(--mono);margin:10px 0;color:var(--ink-dim);
22
22
  overflow-x:auto;white-space:pre}
23
- .md pre code{background:none;padding:0;color:inherit;font-size:inherit}
24
- .md a{color:var(--ruled)}
25
- .md blockquote{margin:10px 0;padding:2px 0 2px 12px;border-left:2px solid var(--line);color:var(--ink-dim)}
23
+ .md pre code{background:none;border:0;padding:0;color:inherit;font-size:inherit}
24
+ .md a{color:var(--accent);text-decoration:underline;text-decoration-color:var(--accent-dim);
25
+ text-underline-offset:3px;text-decoration-thickness:1px}
26
+ .md a:hover{text-decoration-color:var(--accent)}
27
+ .md blockquote{margin:12px 0;padding:2px 0 2px 14px;border-left:3px solid var(--line);color:var(--ink-dim)}
26
28
  .md hr{border:0;border-top:1px solid var(--line);margin:16px 0}
27
29
  .md img{max-width:100%;border-radius:6px;background:var(--panel-2)}
28
30
  .md .ctable{overflow-x:auto;margin:10px 0;max-width:100%}
29
31
  .md table{border-collapse:collapse;font-size:12.5px;width:100%}
30
- .md th,.md td{border:1px solid var(--line);padding:6px 9px;text-align:left;vertical-align:top}
31
- .md th{background:var(--panel-2);color:var(--ink);font-weight:600;white-space:nowrap}
32
+ .md th,.md td{border:1px solid var(--line);padding:7px 10px;text-align:left;vertical-align:top}
33
+ .md th{background:var(--panel-2);color:var(--ink);font-weight:600;white-space:nowrap;
34
+ font-size:11.5px;letter-spacing:.02em}
32
35
  .md td{color:var(--ink-dim)}
33
36
  .md td b,.md td strong{color:var(--ink)}
34
37
  .cbody{margin-top:8px;font-size:13.5px;max-width:74ch}
@@ -70,5 +73,5 @@ pre.yaml{color:var(--ink)}
70
73
  Bordered and inset so it reads as a figure rather than the page bleeding into another one:
71
74
  these are pictures of this same UI, and without a frame the two run together. */
72
75
  .md img{display:block;max-width:100%;height:auto;margin:14px 0;
73
- border:1px solid var(--line);border-radius:8px}
76
+ border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow)}
74
77
  .md img+em,.md p>img+em{display:block}
@@ -1,55 +1,60 @@
1
1
  /* The setup screen, and the copy-a-prompt panel it shares with the authoring screens. */
2
2
 
3
- .step{background:var(--panel);border:1px solid var(--line);border-radius:10px;
4
- padding:18px 20px 20px;margin-bottom:14px}
5
- .step.done{border-color:var(--accent-dim)}
3
+ .step{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);
4
+ padding:20px 22px 22px;margin-bottom:14px;box-shadow:var(--shadow-sm)}
5
+ .step.done{border-color:color-mix(in srgb,var(--accent) 30%,var(--line))}
6
6
  .stephead{display:flex;align-items:center;gap:11px;margin-bottom:10px}
7
- .stephead h2{margin:0;font:600 16px var(--sans);letter-spacing:-.01em}
8
- .stepn{flex:none;width:23px;height:23px;border-radius:50%;display:grid;place-items:center;
9
- background:var(--panel-2);border:1px solid var(--line);color:var(--ink-faint);
7
+ .stephead h2{margin:0;font:600 16px var(--sans);letter-spacing:-.018em}
8
+ .stepn{flex:none;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;
9
+ background:var(--surface-2);border:1px solid transparent;color:var(--ink-dim);
10
10
  font:600 11px var(--mono)}
11
- .step.done .stepn{background:var(--accent-dim);border-color:var(--accent-dim);color:var(--bg)}
11
+ .step.done .stepn{background:var(--accent-fill);border-color:transparent;color:var(--accent-ink)}
12
12
 
13
13
  /* ---------- the form ---------- */
14
14
 
15
15
  .setupform{display:grid;gap:13px;margin:14px 0 18px;max-width:520px}
16
16
  .field{display:grid;gap:4px}
17
17
  .field>span{font:600 12px var(--sans);color:var(--ink-dim)}
18
- .field input,.field select{background:var(--panel-2);border:1px solid var(--line);color:var(--ink);
19
- border-radius:7px;padding:8px 11px;font:13px var(--sans);outline:none;width:100%}
20
- .field input:focus,.field select:focus{border-color:var(--accent-dim)}
18
+ .field input,.field select{background:var(--bg-elev);border:1px solid var(--border-strong);color:var(--ink);
19
+ border-radius:var(--r);padding:8px 11px;font:13px var(--sans);outline:none;width:100%;
20
+ transition:border-color .15s var(--ease),box-shadow .15s var(--ease)}
21
+ .field input:focus,.field select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
21
22
  .field small{color:var(--ink-faint);font-size:11.5px;line-height:1.45}
22
23
 
23
24
  .choices{display:grid;gap:8px;margin:10px 0 4px;max-width:640px}
24
25
  .choice{display:grid;grid-template-columns:auto 1fr;gap:3px 10px;align-items:start;
25
- background:var(--panel-2);border:1px solid var(--line);border-radius:8px;
26
- padding:11px 13px;cursor:pointer}
27
- .choice:has(input:checked){border-color:var(--accent-dim)}
26
+ background:var(--bg-elev);border:1px solid var(--line);border-radius:var(--r);
27
+ padding:12px 14px;cursor:pointer;
28
+ transition:border-color .15s var(--ease),background .15s var(--ease),box-shadow .15s var(--ease)}
29
+ .choice:hover{border-color:var(--border-strong)}
30
+ .choice:has(input:checked){border-color:var(--accent-dim);background:var(--accent-soft)}
28
31
  .choice input{grid-row:1/3;margin-top:3px;accent-color:var(--accent)}
29
32
  .choice b{font:600 13px var(--sans)}
30
33
  .choice small{color:var(--ink-dim);font-size:12px;line-height:1.45}
31
34
  .choice code{grid-column:2;font:10.5px var(--mono);color:var(--ink-faint)}
32
35
 
33
36
  .row{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-top:6px}
34
- .btn{background:var(--panel-2);border:1px solid var(--line);color:var(--ink);border-radius:7px;
35
- padding:8px 13px;font:13px var(--sans);cursor:pointer;text-decoration:none;display:inline-block}
36
- .btn:hover{border-color:var(--accent-dim)}
37
+ .btn{background:var(--surface-2);border:1px solid transparent;color:var(--ink);border-radius:8px;
38
+ padding:7px 14px;font:500 13px var(--sans);cursor:pointer;text-decoration:none;display:inline-block;
39
+ transition:background .15s var(--ease),opacity .15s var(--ease)}
40
+ .btn:hover{background:color-mix(in srgb,var(--surface-2) 88%,var(--ink) 12%)}
41
+ .btn:active{opacity:.8}
37
42
  /* The full accent, not the dim one. Dim on the page background is pale-green-on-white with
38
43
  near-white text: the button that does the main thing on the screen read as disabled. */
39
- .btn.primary{background:var(--accent);border-color:var(--accent);color:var(--bg);font-weight:600}
40
- .btn.primary:hover{filter:brightness(1.08);background:var(--accent);border-color:var(--accent)}
41
- .btn[disabled]{opacity:.5;cursor:default}
44
+ .btn.primary{background:var(--accent-fill);border-color:transparent;color:var(--accent-ink);font-weight:600}
45
+ .btn.primary:hover{background:var(--accent-fill);opacity:.88}
46
+ .btn[disabled]{opacity:.45;cursor:default}
42
47
 
43
- .cmd{background:var(--panel-2);border:1px solid var(--line);border-radius:7px;padding:10px 12px;
48
+ .cmd{background:var(--bg);border:1px solid var(--line);border-radius:var(--r);padding:10px 12px;
44
49
  font:12px/1.6 var(--mono);color:var(--ink);overflow-x:auto;white-space:pre-wrap;
45
50
  word-break:break-word;margin:9px 0}
46
51
 
47
52
  .planout{margin-top:12px}
48
53
  .filelist{display:grid;gap:2px;margin:9px 0;max-height:240px;overflow-y:auto;
49
- background:var(--panel-2);border:1px solid var(--line);border-radius:7px;padding:10px 12px}
54
+ background:var(--bg);border:1px solid var(--line);border-radius:var(--r);padding:10px 12px}
50
55
  .filelist code{font:11px var(--mono);color:var(--ink-dim)}
51
56
 
52
- .manual{background:var(--panel-2);border:1px solid var(--line);border-radius:8px;
57
+ .manual{background:var(--bg-elev);border:1px solid var(--line);border-radius:var(--r);
53
58
  padding:13px 15px;margin:11px 0}
54
59
  .manual b{display:block;font:600 13px var(--sans);margin-bottom:4px}
55
60
  .manual p{margin:0 0 6px;font-size:13px}
@@ -59,22 +64,23 @@
59
64
 
60
65
  .paste{border-top:1px solid var(--line);margin-top:16px;padding-top:15px}
61
66
  .paste h3{margin:0 0 5px;font:600 14px var(--sans)}
62
- .briefpreview{background:var(--panel-2);border:1px solid var(--line);border-radius:7px;
67
+ .briefpreview{background:var(--bg);border:1px solid var(--line);border-radius:var(--r);
63
68
  padding:12px 14px;font:11.5px/1.6 var(--mono);max-height:340px;overflow:auto;
64
69
  white-space:pre-wrap;word-break:break-word;margin:10px 0}
65
- .pastebox{width:100%;background:var(--panel-2);border:1px solid var(--line);color:var(--ink);
66
- border-radius:7px;padding:10px 12px;font:12px/1.6 var(--mono);outline:none;resize:vertical;
70
+ .pastebox{width:100%;background:var(--bg-elev);border:1px solid var(--border-strong);color:var(--ink);
71
+ border-radius:var(--r);padding:10px 12px;font:12px/1.6 var(--mono);outline:none;resize:vertical;
67
72
  margin:4px 0}
68
- .pastebox:focus{border-color:var(--accent-dim)}
73
+ .pastebox:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
69
74
 
70
75
  .pasteresult{margin-top:12px}
71
76
  /* A model that replied in prose is the common case and not the person's mistake, so this reads
72
77
  as a next step rather than as a failure. */
73
- .problem{background:var(--panel-2);border:1px solid var(--warn);border-radius:8px;
78
+ .problem{background:color-mix(in srgb,var(--warn) 9%,transparent);
79
+ border:1px solid transparent;border-radius:var(--r);
74
80
  padding:12px 14px;margin-bottom:10px}
75
81
  .problem b{display:block;font:600 13px var(--sans);margin-bottom:7px}
76
82
 
77
- .pastefile{background:var(--panel-2);border:1px solid var(--line);border-radius:8px;
83
+ .pastefile{background:var(--bg-elev);border:1px solid var(--line);border-radius:var(--r);
78
84
  padding:12px 14px;margin-bottom:10px}
79
85
  .pastefile.saved{border-color:var(--accent-dim)}
80
86
  .pastefile b{font:600 13px var(--mono)}
@@ -83,8 +89,8 @@
83
89
 
84
90
  /* ---------- the doctor checklist ---------- */
85
91
 
86
- .check{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;align-items:start;
87
- padding:10px 0;border-top:1px solid var(--line)}
92
+ .check{display:grid;grid-template-columns:auto 1fr;gap:2px 12px;align-items:start;
93
+ padding:12px 0;border-top:1px solid var(--line)}
88
94
  .check:first-child{border-top:none}
89
95
  .checkmark{font:600 13px var(--mono);line-height:1.5}
90
96
  .check.fail .checkmark{color:var(--err)}
@@ -95,7 +101,7 @@
95
101
  /* ---------- the repo picker ---------- */
96
102
 
97
103
  .repolist{display:grid;gap:1px;max-height:280px;overflow-y:auto;
98
- border:1px solid var(--line);border-radius:7px;background:var(--line)}
104
+ border:1px solid var(--line);border-radius:var(--r);background:var(--line)}
99
105
  /* Scoped to the picker. Bare `.repo` also matched the project label on every inbox row,
100
106
  which is where the white box and the stray padding behind MARKETING came from. */
101
107
  .repolist .repo{display:flex;align-items:center;justify-content:space-between;gap:12px;
@@ -106,6 +112,6 @@
106
112
  .repolist .repo .sub{margin:2px 0 0;font-size:11.5px}
107
113
 
108
114
  /* Which of the two things this is: a new org, or one that already runs roster. */
109
- .verdict{background:var(--panel-2);border:1px solid var(--line);border-radius:7px;
115
+ .verdict{background:var(--accent-soft);border:1px solid transparent;border-radius:var(--r);
110
116
  padding:9px 12px;font-size:12.5px}
111
117
  .verdict p{margin:0}
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>roster</title>
7
7
  <!-- Inline, so a local page with no network still has a tab icon and the console stays clean. -->
8
- <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='7' fill='%237cc7a4'/%3E%3C/svg%3E">
8
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%2334c759'/%3E%3C/svg%3E">
9
9
  <!-- Stylesheets and modules are served from /assets by `roster portal`. No build step:
10
10
  editing one of these files and reloading is the whole edit loop. -->
11
11
  <link rel="stylesheet" href="/assets/css/base.css">
@@ -46,7 +46,11 @@
46
46
  <span class="ic" data-icon="docs"></span> Docs
47
47
  </button>
48
48
 
49
- <div class="sect">Staff</div>
49
+ <!-- The heading is the fold. Collapsed, it carries the count, because a hidden list still
50
+ has to say how much it is hiding. -->
51
+ <button class="sect sectfold" id="stafffold" aria-expanded="false" aria-controls="stafflist">
52
+ <span class="caret" data-icon="chevron"></span>Staff<span class="n" id="staffcount" hidden></span>
53
+ </button>
50
54
  <div id="stafflist"></div>
51
55
 
52
56
  <div class="sidefoot">
@@ -89,6 +89,7 @@ export async function boot() {
89
89
  }
90
90
  onRender(render);
91
91
  applyHash();
92
+ initStaffFold();
92
93
  paintSidebar();
93
94
 
94
95
  $("#inboxnav").onclick = () => { S.view = "inbox"; render(); };
@@ -180,6 +181,42 @@ function learnPeople() {
180
181
  */
181
182
  let paintedRoster = null;
182
183
 
184
+ /**
185
+ * The Staff heading is a fold, and folded it carries the count.
186
+ *
187
+ * It starts folded, so the count is not decoration: a bare "STAFF" heading over nothing is how
188
+ * a loaded org would look if the export came back empty, and those two states have to be
189
+ * tellable apart at a glance. The count sits on the heading rather than beside it so there is
190
+ * one thing to click.
191
+ */
192
+ let staffOpen = true;
193
+
194
+ function paintStaffFold() {
195
+ const b = $("#stafffold");
196
+ if (b) b.setAttribute("aria-expanded", String(staffOpen));
197
+ const list = $("#stafflist");
198
+ if (list) list.hidden = !staffOpen;
199
+ const n = $("#staffcount");
200
+ if (n) {
201
+ n.textContent = "(" + (S.data?.staff?.length ?? 0) + ")";
202
+ n.hidden = staffOpen;
203
+ }
204
+ }
205
+
206
+ function initStaffFold() {
207
+ // Folded until you say otherwise: the count on the heading is enough to know the roster is
208
+ // there, and the four org-wide screens above it are where a session starts.
209
+ staffOpen = store("roster.staffopen") === "1";
210
+ paintStaffFold();
211
+ const b = $("#stafffold");
212
+ if (!b) return;
213
+ b.onclick = () => {
214
+ staffOpen = !staffOpen;
215
+ store("roster.staffopen", staffOpen ? "1" : "0");
216
+ paintStaffFold();
217
+ };
218
+ }
219
+
183
220
  function paintSidebar() {
184
221
  const host = $("#stafflist");
185
222
  host.replaceChildren();
@@ -228,6 +265,9 @@ function paintSidebar() {
228
265
 
229
266
  host.append(head, views);
230
267
  }
268
+
269
+ // A refresh can hire or retire someone, and the folded heading is where that number shows.
270
+ paintStaffFold();
231
271
  }
232
272
 
233
273
  /** Which rows are lit, and which staff member is unfolded. */
@@ -36,7 +36,9 @@ export const S = {
36
36
  loadedAt: null,
37
37
 
38
38
  staffHandle: null,
39
- view: "brain",
39
+ /* What is waiting on you, not whose brain you read last. The org-wide screens are where a
40
+ session starts, which is also why the Staff list below them opens folded. */
41
+ view: "inbox",
40
42
  /** The inbox filter box. Brain and "what changed" keep their own, below. */
41
43
  query: "",
42
44
 
@@ -73,9 +75,10 @@ export const S = {
73
75
 
74
76
  /* The sidebar badge counts open items, not everything loaded. Once the inbox could show
75
77
  closed work the two stopped being the same number, and a badge that says 135 when 34 things
76
- need you is worse than no badge. */
78
+ need you is worse than no badge. Pull requests are excluded for the same reason: they are
79
+ Pending work's, and counting them here would bill you twice for the same pile. */
77
80
  export const openCount = () =>
78
- (S.inbox?.items ?? []).filter((i) => i.state === "OPEN").length;
81
+ (S.inbox?.items ?? []).filter((i) => i.state === "OPEN" && i.kind !== "pr").length;
79
82
 
80
83
  /** The same, for the screen that is only pull requests. */
81
84
  export const openPrCount = () =>
@@ -53,7 +53,7 @@ function drawGraph(canvas, wrap, s) {
53
53
  }
54
54
  const groups = [...members.keys()].sort((a, b) => members.get(b).length - members.get(a).length);
55
55
 
56
- const PALETTE = ["#7cc7a4","#7aa2f7","#c99bd6","#d8a657","#6fc3c9","#e07b7b","#b3c46b","#e0a1c0","#9d9ff5","#8fb0d9"];
56
+ const PALETTE = ["#30d158","#0a84ff","#bf5af2","#ff9f0a","#64d2ff","#ff453a","#ffcc00","#ff375f","#5e5ce6","#ac8e68"];
57
57
  const colour = new Map(groups.map((g, i) => [g, PALETTE[i % PALETTE.length]]));
58
58
 
59
59
  // The ring grows with the number of groups so they never crowd, and each group keeps its