@bongos/core 1.19.781 → 1.19.783
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/.bongos-core.json +41 -36
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +50 -3
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +29 -27
- package/docs/copy-registry.json +86 -68
- package/docs/module-api-changelog.md +4 -0
- package/modules/builder-settings/builder-needs.js +9 -3
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +2 -0
- package/modules/hall-ui/public/collab-lib.js +94 -2
- package/modules/hall-ui/public/collab.css +110 -5
- package/modules/hall-ui/public/collab.html +51 -37
- package/modules/hall-ui/public/collab.js +153 -60
- package/modules/hall-ui/public/shell.js +3 -1
- package/modules/lifecycle/help-requests.js +24 -2
- package/modules/lifecycle/routes/help-requests.js +11 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/start.js +26 -3
- package/src/module-api.js +1 -1
- package/tests/builder_needs.mjs +24 -0
- package/tests/collab_page.mjs +119 -0
- package/tests/hall_collab_world.mjs +7 -3
- package/tests/hall_nav.mjs +47 -0
- package/tests/help_requests.mjs +31 -0
- package/tests/social_mode.mjs +3 -1
- package/tests/start_inbox_count.mjs +112 -0
|
@@ -110,12 +110,18 @@ function helpRequestsNeed(ctx) {
|
|
|
110
110
|
const verb = n === 1 ? 'has' : 'have';
|
|
111
111
|
return {
|
|
112
112
|
id: 'help_requests',
|
|
113
|
+
// The number, machine-readable (task 1003942) — the field boardVotesNeed
|
|
114
|
+
// already carries, and the whole badge contract: shell.js names this need's
|
|
115
|
+
// id on the Collab nav item and reads this count into the badge.
|
|
116
|
+
count: n,
|
|
113
117
|
title: n === 1 ? 'Someone asked for your help' : 'People asked for your help',
|
|
114
118
|
what: 'A builder who is stuck addressed a request to your craft, or to you by name.',
|
|
115
119
|
state: 'action_needed',
|
|
116
120
|
severity: 'info',
|
|
117
121
|
message: `${people} ${verb} asked for help you can give. Answering is optional and declining costs nothing.`,
|
|
118
|
-
|
|
122
|
+
// Collab is its own page since task 1003172; the retired '/#/collab' hash
|
|
123
|
+
// form pointed at a view the index no longer routes.
|
|
124
|
+
action: { label: n === 1 ? 'See the request' : 'See the requests', href: '/collab' },
|
|
119
125
|
};
|
|
120
126
|
}
|
|
121
127
|
|
|
@@ -152,7 +158,7 @@ function taskRecommendationsNeed(ctx) {
|
|
|
152
158
|
state: 'action_needed',
|
|
153
159
|
severity: 'info',
|
|
154
160
|
message: `${subject} ${verb} recommended to you. Nothing has been assigned — you would still claim it yourself, and declining costs nothing.`,
|
|
155
|
-
action: { label: n === 1 ? 'See the recommendation' : 'See the recommendations', href: '
|
|
161
|
+
action: { label: n === 1 ? 'See the recommendation' : 'See the recommendations', href: '/collab' },
|
|
156
162
|
};
|
|
157
163
|
}
|
|
158
164
|
|
|
@@ -230,7 +236,7 @@ function mingleIntroductionsNeed(ctx) {
|
|
|
230
236
|
severity: 'info',
|
|
231
237
|
message: `${subject} waiting on you. Saying no costs nothing at all — no rank, no karma, no streak, `
|
|
232
238
|
+ 'and it does not change how often you are paired again.',
|
|
233
|
-
action: { label: n === 1 ? 'See the introduction' : 'See the introductions', href: '
|
|
239
|
+
action: { label: n === 1 ? 'See the introduction' : 'See the introductions', href: '/collab' },
|
|
234
240
|
};
|
|
235
241
|
}
|
|
236
242
|
|
|
@@ -468,6 +468,8 @@ function createClient(opts = {}) {
|
|
|
468
468
|
getHealthz: (args) => request("GET", "/healthz", { hasBody: false }, args),
|
|
469
469
|
},
|
|
470
470
|
"helpRequests": {
|
|
471
|
+
// GET /help-requests — rank: any-builder — GET /help-requests
|
|
472
|
+
getHelpRequests: (args) => request("GET", "/help-requests", { hasBody: false }, args),
|
|
471
473
|
// POST /help-requests — rank: any-builder — POST /help-requests
|
|
472
474
|
postHelpRequests: (args) => request("POST", "/help-requests", { hasBody: true }, args),
|
|
473
475
|
// PATCH /help-requests/{id} — rank: any-builder — PATCH /help-requests/:id
|
|
@@ -128,10 +128,102 @@
|
|
|
128
128
|
return { id: 'other', label: 'asked' };
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// ---- the FACES (task 1003942, owner report: "doesn't feel collaborative") ----
|
|
132
|
+
//
|
|
133
|
+
// A board of builders reaching builders should read as people before it reads
|
|
134
|
+
// as prose, so every row now leads with a face and the top strip stacks the
|
|
135
|
+
// faces of everyone waiting. These are the pure halves: which person a row
|
|
136
|
+
// leads with, what to draw when there is no picture, and the figure strip's
|
|
137
|
+
// numbers. collab.js only turns the result into markup.
|
|
138
|
+
|
|
139
|
+
// Defence in depth on an avatar src (roster.js's rule): escaping alone cannot
|
|
140
|
+
// stop a non-http scheme reaching an attribute the browser resolves, so a src
|
|
141
|
+
// this page did not vouch for renders as the initial instead.
|
|
142
|
+
function httpUrl(u) {
|
|
143
|
+
return /^https?:\/\//i.test(String(u || '')) ? String(u) : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The letter a faceless person shows: the first character of their name,
|
|
147
|
+
// uppercased, with a leading @ or # dropped. Never empty — '?' stands in.
|
|
148
|
+
function initialOf(name) {
|
|
149
|
+
var n = String(name == null ? '' : name).replace(/^[@#]/, '').trim();
|
|
150
|
+
return (n.charAt(0) || '?').toUpperCase();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// The person a row LEADS with. Incoming ('in') is always the asker — the
|
|
154
|
+
// human waiting on an answer. Outgoing ('out') is who you asked: the one
|
|
155
|
+
// person on a by-name ask, else the craft, drawn as a lettered disc so a craft
|
|
156
|
+
// ask never borrows a person's face. Same dir contract as directionLine.
|
|
157
|
+
function faceOf(req, dir) {
|
|
158
|
+
if (!req) return null;
|
|
159
|
+
if (dir === 'out') {
|
|
160
|
+
if (req.needs_builder_id != null) {
|
|
161
|
+
var to = req.needs_builder_name || req.needs_builder_login || '';
|
|
162
|
+
return {
|
|
163
|
+
kind: 'person', id: req.needs_builder_id, name: to,
|
|
164
|
+
avatar: httpUrl(req.needs_builder_avatar_url), initial: initialOf(to),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
var craft = req.needs_craft ? String(req.needs_craft) : '';
|
|
168
|
+
return { kind: 'craft', id: null, name: craft ? craft + 's' : 'anyone', avatar: null, initial: craft ? initialOf(craft) : '?' };
|
|
169
|
+
}
|
|
170
|
+
var who = askerOf(req);
|
|
171
|
+
return {
|
|
172
|
+
kind: 'person', id: req.requested_by != null ? req.requested_by : null, name: who,
|
|
173
|
+
avatar: httpUrl(req.requested_by_avatar_url), initial: initialOf(who),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// The DISTINCT people asking, in list order — the face stack in the top strip.
|
|
178
|
+
// Two asks from one person are one face; keyed by id when the server sent one,
|
|
179
|
+
// by name otherwise, so an older server still dedupes.
|
|
180
|
+
function askers(rows) {
|
|
181
|
+
var seen = {};
|
|
182
|
+
var out = [];
|
|
183
|
+
for (var i = 0; i < (rows || []).length; i += 1) {
|
|
184
|
+
var f = faceOf(rows[i], 'in');
|
|
185
|
+
if (!f) continue;
|
|
186
|
+
var key = f.id != null ? 'id:' + f.id : 'name:' + f.name;
|
|
187
|
+
if (seen[key]) continue;
|
|
188
|
+
seen[key] = true;
|
|
189
|
+
out.push(f);
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Newest first — the order the three-block board reads in (owner direction,
|
|
195
|
+
// task 1003942: "only show the three most recent, then expand"). The ageing
|
|
196
|
+
// criterion is still met by the band on every row; this is a display order,
|
|
197
|
+
// and the oldest-first comparator stays for the callers that want risk first.
|
|
198
|
+
function byNewestFirst(a, b) {
|
|
199
|
+
return byAgeOldestFirst(b, a);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// The first N rows are shown, the rest wait behind "show all": a pure split so
|
|
203
|
+
// the count on the button and the rows it reveals come from one place.
|
|
204
|
+
function splitShown(rows, n) {
|
|
205
|
+
var list = rows || [];
|
|
206
|
+
var cap = typeof n === 'number' && n >= 0 ? n : list.length;
|
|
207
|
+
return { shown: list.slice(0, cap), hidden: list.slice(cap) };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The THIRD-PERSON line, for the board of everyone's asks: neither "you" nor
|
|
211
|
+
// "asked you". "Gamma asked builders" / "Alpha asked Beta by name".
|
|
212
|
+
function boardLine(req) {
|
|
213
|
+
if (!req) return '';
|
|
214
|
+
var who = askerOf(req) || 'Someone';
|
|
215
|
+
if (req.needs_builder_id != null) {
|
|
216
|
+
var to = req.needs_builder_name || req.needs_builder_login;
|
|
217
|
+
return to ? who + ' asked ' + to + ' by name' : who + ' asked one person';
|
|
218
|
+
}
|
|
219
|
+
return req.needs_craft ? who + ' asked ' + req.needs_craft + 's' : who + ' asked';
|
|
220
|
+
}
|
|
221
|
+
|
|
131
222
|
window.OTBCollabLib = {
|
|
132
|
-
BANDS: BANDS, ageBand: ageBand, byAgeOldestFirst: byAgeOldestFirst,
|
|
223
|
+
BANDS: BANDS, ageBand: ageBand, byAgeOldestFirst: byAgeOldestFirst, byNewestFirst: byNewestFirst,
|
|
133
224
|
askerOf: askerOf, contextOf: contextOf,
|
|
134
|
-
directionLine: directionLine,
|
|
225
|
+
directionLine: directionLine, boardLine: boardLine,
|
|
135
226
|
titleOf: titleOf, groupOf: groupOf,
|
|
227
|
+
httpUrl: httpUrl, initialOf: initialOf, faceOf: faceOf, askers: askers, splitShown: splitShown,
|
|
136
228
|
};
|
|
137
229
|
})();
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/* modules/hall-ui/public/collab.css — the Collab page (task 1003172 / C02),
|
|
2
|
-
rebuilt on the hall's row archetype by task 1003471
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
rebuilt on the hall's row archetype by task 1003471, and made a THREE-BLOCK
|
|
3
|
+
room of people by task 1003942 (owner direction: "way too text heavy, doesn't
|
|
4
|
+
feel collaborative" — asked of you left, your asks right, every open ask
|
|
5
|
+
below, each condensed to the newest three with a show-all). The panel is the
|
|
6
|
+
shell's .scroll, the two-up is the shell's .view-zone--band, the row is
|
|
7
|
+
oversight.css's .ov-row--lead (a face, then the words, then the state), the
|
|
8
|
+
age is a .fact-pill, the context a .fact-chip, the expander the shell's
|
|
9
|
+
.btn-ghost. This sheet holds only what Collab has and no other page does: the
|
|
10
|
+
colour of each age band, the faces, the block head, the lit personal call, and
|
|
11
|
+
the foot.
|
|
6
12
|
Tokens only from style.css's real set: inventing one renders as nothing,
|
|
7
13
|
which is how copy-desk.css once ended up with invisible fallbacks (idea
|
|
8
|
-
1000821).
|
|
14
|
+
1000821). Every colour here is a var() of the fifteen or the hall's measured
|
|
15
|
+
status vocabulary, so the dark tier needs no twin in this file. */
|
|
9
16
|
|
|
10
17
|
/* THE AGEING SIGNAL. The criterion asks that a request nobody takes be visibly
|
|
11
18
|
aging rather than silently dead — so the age is a state pill in the row's
|
|
@@ -18,3 +25,101 @@
|
|
|
18
25
|
.cb-age--waiting { color: var(--ink-soft); }
|
|
19
26
|
.cb-age--aging { color: var(--warn); }
|
|
20
27
|
.cb-age--stale { color: var(--danger); font-weight: 600; }
|
|
28
|
+
|
|
29
|
+
/* ---- the blocks ------------------------------------------------------------------
|
|
30
|
+
The top band is the shell's two-up grid (it stacks to one column at the
|
|
31
|
+
shell's phone breakpoint). A block is the shell's panel with a head row: the
|
|
32
|
+
heading, its count in mono, and the faces of whoever is asking on the right. */
|
|
33
|
+
.cb-band { margin: 0 0 var(--sp-md); }
|
|
34
|
+
.cb-block__head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 6px; }
|
|
35
|
+
.cb-block__head .scroll__h { margin: 0; }
|
|
36
|
+
.cb-block__n {
|
|
37
|
+
font-family: var(--font-mono);
|
|
38
|
+
font-size: 13px;
|
|
39
|
+
font-weight: 400;
|
|
40
|
+
letter-spacing: 0;
|
|
41
|
+
color: var(--ink-faint);
|
|
42
|
+
font-variant-numeric: tabular-nums;
|
|
43
|
+
margin-left: 6px;
|
|
44
|
+
}
|
|
45
|
+
.cb-block__head .cb-faces { margin-left: auto; }
|
|
46
|
+
|
|
47
|
+
/* "Show all N" / "Show fewer": the shell's ghost pill, on its own line under
|
|
48
|
+
the rows, so the condensed block ends on its door to the rest. */
|
|
49
|
+
.cb-more { margin-top: 12px; }
|
|
50
|
+
|
|
51
|
+
/* ---- the face -----------------------------------------------------------------
|
|
52
|
+
A row leads with WHO: a 36px disc, the picture when the server sent one, the
|
|
53
|
+
initial on the glass when it did not (people.css's .dir-avatar, at the row's
|
|
54
|
+
size). A craft ask wears the craft's letter in the label face, so it never
|
|
55
|
+
passes for a person. Decorative — the name beside it is the door. */
|
|
56
|
+
.cb-face {
|
|
57
|
+
width: 36px; height: 36px; flex: none;
|
|
58
|
+
border-radius: 50%;
|
|
59
|
+
object-fit: cover;
|
|
60
|
+
background: var(--skeleton);
|
|
61
|
+
border: 1px solid var(--rule);
|
|
62
|
+
margin-top: 1px;
|
|
63
|
+
}
|
|
64
|
+
.cb-face--initial {
|
|
65
|
+
display: inline-grid; place-items: center;
|
|
66
|
+
font-size: 14px; font-weight: 500; line-height: 1;
|
|
67
|
+
color: var(--ink-soft);
|
|
68
|
+
background: var(--glass-strong);
|
|
69
|
+
}
|
|
70
|
+
.cb-face--craft {
|
|
71
|
+
font-family: var(--label-face);
|
|
72
|
+
font-size: 12px;
|
|
73
|
+
letter-spacing: 0;
|
|
74
|
+
color: var(--ink-faint);
|
|
75
|
+
background: none;
|
|
76
|
+
border-style: dashed;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* The stack of everyone asking: 24px faces at -7px overlap, each ringed in the
|
|
80
|
+
panel ground (the poster card's face-row grammar). */
|
|
81
|
+
.cb-faces { display: inline-flex; align-items: center; }
|
|
82
|
+
.cb-faces .cb-face {
|
|
83
|
+
width: 24px; height: 24px;
|
|
84
|
+
margin: 0 0 0 -7px;
|
|
85
|
+
border: 2px solid var(--bg-card);
|
|
86
|
+
font-size: 10px;
|
|
87
|
+
}
|
|
88
|
+
.cb-faces .cb-face:first-child { margin-left: 0; }
|
|
89
|
+
.cb-faces .cb-face--initial { background: var(--glass-strong); }
|
|
90
|
+
|
|
91
|
+
/* ---- the personal call ------------------------------------------------------------
|
|
92
|
+
An ask addressed to you BY NAME is the one live thing on the board — the
|
|
93
|
+
personal call this reader owes an answer — so its direction line takes the
|
|
94
|
+
accent ink. Accent goes only on what is live, and this is. */
|
|
95
|
+
.cb-row--live .ov-row__facts > .ov-fact { color: var(--accent-ink); }
|
|
96
|
+
|
|
97
|
+
/* ---- the rows, as people -------------------------------------------------------
|
|
98
|
+
The archetype's --lead grid puts the face in its own column. A row lights on
|
|
99
|
+
hover with the glass the family's button rows use, so the board feels awake
|
|
100
|
+
without any motion; the Kill Switch has nothing to kill here. */
|
|
101
|
+
.cb-row { padding-left: 10px; padding-right: 10px; margin: 0 -10px; border-radius: var(--radius-sm); }
|
|
102
|
+
.cb-row:hover { background: var(--glass); }
|
|
103
|
+
.cb-row .ov-row__facts a { color: var(--accent-ink); }
|
|
104
|
+
/* Inside the two condensed blocks a row has half the page: the state and the
|
|
105
|
+
actions drop UNDER the words as one line (the archetype's own phone shape),
|
|
106
|
+
the date yields to the age pill, and the two-up itself becomes one column
|
|
107
|
+
before the shell's phone breakpoint, at the width where two half-rows stop
|
|
108
|
+
holding a sentence. The wide block below keeps the archetype's two columns. */
|
|
109
|
+
.cb-band .ov-row--lead { grid-template-columns: auto minmax(0, 1fr); row-gap: 8px; }
|
|
110
|
+
.cb-band .ov-row--lead > .ov-row__right {
|
|
111
|
+
grid-column: 2;
|
|
112
|
+
flex-direction: row;
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
align-items: center;
|
|
115
|
+
text-align: left;
|
|
116
|
+
}
|
|
117
|
+
.cb-band .ov-row__actions { justify-content: flex-start; }
|
|
118
|
+
.cb-band .ov-row__when { display: none; }
|
|
119
|
+
@media (max-width: 1100px) {
|
|
120
|
+
.cb-band { grid-template-columns: 1fr; }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ---- empties and the foot -------------------------------------------------- */
|
|
124
|
+
.cb-empty p { font-size: 14px; }
|
|
125
|
+
.cb-foot { margin: var(--sp-sm) 0 0; font-size: 12.5px; color: var(--ink-faint); max-width: var(--measure); }
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Collab — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="
|
|
7
|
+
<meta name="description" content="Builders asking builders for help: what is waiting on you, what you asked, and every open ask across the project.">
|
|
8
8
|
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -20,30 +20,53 @@
|
|
|
20
20
|
<div class="app-body">
|
|
21
21
|
<header class="app-topbar" id="app-topbar"></header>
|
|
22
22
|
<div class="app-content">
|
|
23
|
-
<!-- The board page head: the page's own name in the LABEL voice
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
<!-- The board page head: the page's own name in the LABEL voice. The sub
|
|
24
|
+
line is the one sentence of orientation the page keeps: who reaches
|
|
25
|
+
you, written by collab.js. -->
|
|
26
26
|
<div class="page-head page-head--board">
|
|
27
27
|
<h1 class="page-head__h">Collab</h1>
|
|
28
28
|
<p class="page-head__sub" id="cb-sub">Loading…</p>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
|
+
<!-- THREE BLOCKS (owner direction, task 1003942). Top: ASKED OF YOU on the
|
|
32
|
+
left and YOUR ASKS on the right, each condensed to the three newest
|
|
33
|
+
with a "show all" that reveals the rest. Below, one wide block: every
|
|
34
|
+
open ask in the project. Each block's head carries its count and,
|
|
35
|
+
where people are asking, their faces. The rows are the hall's row
|
|
36
|
+
archetype (oversight.css) led by a face; collab.js fills them. -->
|
|
31
37
|
<main id="hall-main">
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
<div class="view-zone view-zone--band cb-band">
|
|
39
|
+
<section class="scroll cb-block" aria-labelledby="cb-for-me-h">
|
|
40
|
+
<div class="cb-block__head">
|
|
41
|
+
<h2 class="scroll__h" id="cb-for-me-h">Asked of you <span class="cb-block__n" id="cb-for-me-n"></span></h2>
|
|
42
|
+
<span class="cb-faces" id="cb-for-me-faces" aria-hidden="true"></span>
|
|
43
|
+
</div>
|
|
44
|
+
<ul class="ov-rows" id="cb-for-me"></ul>
|
|
45
|
+
<div class="empty-state cb-empty" id="cb-for-me-empty" hidden>
|
|
46
|
+
<p>Nobody is waiting on you. Requests reach you by craft —
|
|
47
|
+
<a href="/builders/settings">set yours in Settings</a>.</p>
|
|
48
|
+
</div>
|
|
49
|
+
<button class="btn-ghost cb-more" id="cb-for-me-more" type="button" aria-expanded="false" aria-controls="cb-for-me" hidden></button>
|
|
50
|
+
</section>
|
|
51
|
+
|
|
52
|
+
<section class="scroll cb-block" aria-labelledby="cb-mine-h">
|
|
53
|
+
<div class="cb-block__head">
|
|
54
|
+
<h2 class="scroll__h" id="cb-mine-h">Your asks <span class="cb-block__n" id="cb-mine-n"></span></h2>
|
|
55
|
+
</div>
|
|
56
|
+
<ul class="ov-rows" id="cb-mine"></ul>
|
|
57
|
+
<div class="empty-state cb-empty" id="cb-mine-empty" hidden><p>You have no open asks.</p></div>
|
|
58
|
+
<button class="btn-ghost cb-more" id="cb-mine-more" type="button" aria-expanded="false" aria-controls="cb-mine" hidden></button>
|
|
59
|
+
</section>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<section class="scroll cb-block cb-block--all" aria-labelledby="cb-all-h">
|
|
63
|
+
<div class="cb-block__head">
|
|
64
|
+
<h2 class="scroll__h" id="cb-all-h">All open asks <span class="cb-block__n" id="cb-all-n"></span></h2>
|
|
65
|
+
<span class="cb-faces" id="cb-all-faces" aria-hidden="true"></span>
|
|
46
66
|
</div>
|
|
67
|
+
<ul class="ov-rows" id="cb-all"></ul>
|
|
68
|
+
<div class="empty-state cb-empty" id="cb-all-empty" hidden><p>Nobody has an open ask right now.</p></div>
|
|
69
|
+
<button class="btn-ghost cb-more" id="cb-all-more" type="button" aria-expanded="false" aria-controls="cb-all" hidden></button>
|
|
47
70
|
</section>
|
|
48
71
|
|
|
49
72
|
<!-- SOCIAL MODE ONLY (task 1003178 / C08). Not a page and not a nav item:
|
|
@@ -52,28 +75,19 @@
|
|
|
52
75
|
from /me, and collab.js reveals this section from that. With social
|
|
53
76
|
off the page is exactly what it was, which is the point: the hall
|
|
54
77
|
changes, it does not gain a destination. -->
|
|
55
|
-
<section class="scroll" id="cb-mingle-section" aria-labelledby="cb-mingle-h" hidden>
|
|
78
|
+
<section class="scroll cb-block" id="cb-mingle-section" aria-labelledby="cb-mingle-h" hidden>
|
|
56
79
|
<h2 class="scroll__h" id="cb-mingle-h">Introductions</h2>
|
|
57
|
-
<p class="scroll__lede" id="cb-mingle-note">
|
|
58
|
-
Mingle pairs you with someone from a different craft. Saying no costs nothing
|
|
59
|
-
at all — no rank, no karma, no streak, and it does not change how often you
|
|
60
|
-
are paired again.
|
|
61
|
-
</p>
|
|
80
|
+
<p class="scroll__lede" id="cb-mingle-note">Mingle pairs you with someone from another craft. Saying no costs nothing.</p>
|
|
62
81
|
<ul class="ov-rows" id="cb-mingle"></ul>
|
|
63
|
-
<div class="empty-state" id="cb-mingle-empty" hidden>
|
|
64
|
-
<p>No introductions right now.
|
|
82
|
+
<div class="empty-state cb-empty" id="cb-mingle-empty" hidden>
|
|
83
|
+
<p>No introductions right now. One appears here when a pairing round runs.</p>
|
|
65
84
|
</div>
|
|
66
85
|
</section>
|
|
67
86
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
helped, or withdraw it when you no longer need it.
|
|
73
|
-
</p>
|
|
74
|
-
<ul class="ov-rows" id="cb-mine"></ul>
|
|
75
|
-
<div class="empty-state" id="cb-mine-empty" hidden><p>You have no open asks.</p></div>
|
|
76
|
-
</section>
|
|
87
|
+
<!-- The one sentence of rules, said ONCE, at the foot. Answering is
|
|
88
|
+
optional by design (ADR 0187 §3), and the page still says so; it
|
|
89
|
+
just stops saying it first. -->
|
|
90
|
+
<p class="cb-foot">Answering is optional and declining costs nothing. Nobody is assigned by being asked.</p>
|
|
77
91
|
</main>
|
|
78
92
|
</div>
|
|
79
93
|
</div>
|
|
@@ -84,7 +98,7 @@
|
|
|
84
98
|
<script src="/builders/shell.js?v=2026-07-02-shell1"></script>
|
|
85
99
|
<script src="/builders/palette.js" defer></script>
|
|
86
100
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
87
|
-
<script src="/builders/collab-lib.js?v=2026-09-
|
|
88
|
-
<script src="/builders/collab.js?v=2026-09-
|
|
101
|
+
<script src="/builders/collab-lib.js?v=2026-09-16-blocks"></script>
|
|
102
|
+
<script src="/builders/collab.js?v=2026-09-16-blocks"></script>
|
|
89
103
|
</body>
|
|
90
104
|
</html>
|