@bongos/core 1.24.61 → 1.24.63
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 +54 -44
- package/docs/copy-inventory.md +152 -151
- package/docs/copy-registry.json +232 -203
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +1 -0
- package/docs/page-readings.json +281 -249
- package/modules/hall-ui/public/approval-queue.css +5 -3
- package/modules/hall-ui/public/controls.css +10 -0
- package/modules/hall-ui/public/gate.js +11 -2
- package/modules/hall-ui/public/hall-dialog.js +50 -2
- package/modules/hall-ui/public/hall-kit.js +8 -3
- package/modules/hall-ui/public/roster.js +13 -5
- package/modules/hall-ui/public/settings-passkeys.js +4 -1
- package/modules/hall-ui/public/studio.css +19 -20
- package/modules/hall-ui/public/studio.html +1 -2
- package/modules/hall-ui/public/studio.js +6 -2
- package/modules/hall-ui/public/style.css +17 -5
- package/modules/hall-ui/public/thinking.css +18 -1
- package/modules/hall-ui/records/studio.md +2 -1
- package/modules/hall-ui/specimens/components.css +7 -0
- package/modules/npm-release/public/work.js +6 -1
- package/modules/public-landing/public/assets/cosmos.css +36 -13
- package/modules/public-landing/public/index.html +9 -1
- package/modules/public-landing/public/projects.html +11 -3
- package/modules/raise/page.js +3 -1
- package/modules/raise/public/edit.js +10 -3
- package/modules/raise/public/raise.css +31 -4
- package/modules/render-deploy/public/deploy.js +2 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/module-api.js +1 -1
- package/tests/hall_dialog.mjs +169 -17
- package/tests/hall_studio_home.mjs +8 -1
- package/tests/hall_topbar_320.mjs +145 -0
- package/tests/landing_shared_controls.mjs +157 -0
- package/tests/npm_release_release.mjs +1 -1
- package/tests/projects_hub.mjs +1 -1
- package/tests/projects_hub_pre_uat.mjs +3 -1
- package/tests/roster_government_columns.mjs +3 -1
|
@@ -67,9 +67,11 @@ html.aq-open { overflow: hidden; }
|
|
|
67
67
|
min-width: 0;
|
|
68
68
|
box-sizing: border-box;
|
|
69
69
|
padding: 26px 30px;
|
|
70
|
-
|
|
70
|
+
/* the shared dialog's corner and hairline (controls.css .hall-dialog, task
|
|
71
|
+
1005137): the glass is the artboard's, the shape is the hall's one dialog */
|
|
72
|
+
border-radius: var(--ds-radius-panel);
|
|
71
73
|
background: var(--aq-glass);
|
|
72
|
-
border:
|
|
74
|
+
border: var(--line) solid var(--aq-glass-edge);
|
|
73
75
|
backdrop-filter: blur(30px) saturate(160%);
|
|
74
76
|
-webkit-backdrop-filter: blur(30px) saturate(160%);
|
|
75
77
|
box-shadow: inset 0 1px 0 var(--aq-glass-rim), var(--ds-shadow-pop);
|
|
@@ -309,7 +311,7 @@ html.aq-open { overflow: hidden; }
|
|
|
309
311
|
.aq-overlay { padding: 8px; align-items: stretch; --aq-scrim: rgba(4, 5, 9, .88); }
|
|
310
312
|
html[data-theme] .aq-overlay,
|
|
311
313
|
html:not([data-theme]) .aq-overlay { --aq-scrim: rgba(4, 5, 9, .88); }
|
|
312
|
-
.aq-dialog { width: min(100%, calc(100vw - 16px)); height: min(100%, calc(100dvh - 16px)); padding: 18px 16px;
|
|
314
|
+
.aq-dialog { width: min(100%, calc(100vw - 16px)); height: min(100%, calc(100dvh - 16px)); padding: 18px 16px; overflow-y: auto; gap: 14px; }
|
|
313
315
|
.aq-dialog > * { flex-shrink: 0; }
|
|
314
316
|
.aq-head { flex-wrap: wrap; row-gap: 10px; }
|
|
315
317
|
.aq-eyebrow { order: 1; flex: 1 1 auto; }
|
|
@@ -314,6 +314,16 @@ select option { background: var(--bg-card); color: var(--ink); }
|
|
|
314
314
|
color: var(--ink-soft);
|
|
315
315
|
}
|
|
316
316
|
.hall-dialog__body p:last-child { margin-bottom: 0; }
|
|
317
|
+
/* the prompt's one field (OTBDialog.prompt, task 1005136): the hall's field,
|
|
318
|
+
the dialog's full width, under its label */
|
|
319
|
+
.hall-dialog__label {
|
|
320
|
+
display: block;
|
|
321
|
+
margin: var(--ds-space-xs) 0 var(--ds-space-hair);
|
|
322
|
+
font-size: var(--ds-text-sm);
|
|
323
|
+
color: var(--ink-soft);
|
|
324
|
+
}
|
|
325
|
+
.hall-dialog__field { display: block; width: 100%; margin-top: var(--ds-space-xs); }
|
|
326
|
+
.hall-dialog__label + .hall-dialog__field { margin-top: 0; }
|
|
317
327
|
.hall-dialog__actions {
|
|
318
328
|
display: flex;
|
|
319
329
|
flex-wrap: wrap;
|
|
@@ -284,7 +284,11 @@
|
|
|
284
284
|
// questions. Turning it ON needs no reason — the allowlist already is one.
|
|
285
285
|
let reason = null;
|
|
286
286
|
if (on) {
|
|
287
|
-
reason = window.prompt(
|
|
287
|
+
reason = await window.OTBDialog.prompt({
|
|
288
|
+
title: 'Why are you turning the runner off?',
|
|
289
|
+
label: 'Shown to anyone who asks why it is idle',
|
|
290
|
+
confirmLabel: 'Turn off',
|
|
291
|
+
});
|
|
288
292
|
if (reason === null) return; // cancelled — not the same as an empty reason
|
|
289
293
|
}
|
|
290
294
|
try {
|
|
@@ -414,7 +418,12 @@
|
|
|
414
418
|
const pausing = btn.getAttribute('data-paused') !== '1';
|
|
415
419
|
let reason = null;
|
|
416
420
|
if (pausing) {
|
|
417
|
-
reason = window.prompt(
|
|
421
|
+
reason = await window.OTBDialog.prompt({
|
|
422
|
+
title: 'Why are you pausing this builder’s runner?',
|
|
423
|
+
label: 'Shown to them',
|
|
424
|
+
confirmLabel: 'Pause',
|
|
425
|
+
trigger: btn,
|
|
426
|
+
});
|
|
418
427
|
if (reason === null) return;
|
|
419
428
|
}
|
|
420
429
|
try {
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
The UI audit found six dialog builds (attn, cd, mw, aq, ov, st), each with
|
|
5
5
|
its own scrim, layer, Escape handling and focus rules, and fourteen
|
|
6
6
|
window.confirm() calls whose browser box neither looks like the hall nor
|
|
7
|
-
reads in its voice. This is the one of each that replaces them
|
|
7
|
+
reads in its voice. This is the one of each that replaces them; its prompt
|
|
8
|
+
replaces the browser's text box the same way (task 1005136).
|
|
8
9
|
|
|
9
10
|
window.OTBDialog:
|
|
10
11
|
confirm({ title, body, confirmLabel, cancelLabel, danger }) → Promise<boolean>
|
|
@@ -12,6 +13,14 @@
|
|
|
12
13
|
starts a new paragraph. Resolves true only on the confirm button;
|
|
13
14
|
Cancel, the close mark, Escape and a click on the scrim all resolve
|
|
14
15
|
false. A `danger` confirm opens with focus on Cancel.
|
|
16
|
+
prompt({ title, body, label, value, placeholder, confirmLabel, cancelLabel, required, maxLength })
|
|
17
|
+
→ Promise<string|null>
|
|
18
|
+
The branded prompt (task 1005136): the confirm with one text field.
|
|
19
|
+
Resolves the text as typed on the confirm button or Enter, and null
|
|
20
|
+
on Cancel, the close mark, Escape or the scrim — so a caller can tell
|
|
21
|
+
"cancelled" from "left empty". `required` keeps the confirm button
|
|
22
|
+
off while the field is blank. The field is labelled by `label`, or by
|
|
23
|
+
the title when there is none; it opens focused with its text selected.
|
|
15
24
|
open({ title, body, actions, wide, className, initialFocus }) → { el, close(value), result }
|
|
16
25
|
A dialog built here and removed on close; `result` resolves with the
|
|
17
26
|
pressed action's `value` (or null when dismissed).
|
|
@@ -249,7 +258,46 @@
|
|
|
249
258
|
return dlg.result.then((v) => v === true);
|
|
250
259
|
}
|
|
251
260
|
|
|
252
|
-
|
|
261
|
+
// The prompt's confirm action resolves to this, never to a value a caller could
|
|
262
|
+
// pass, so "use what was typed" cannot be confused with an action's own value.
|
|
263
|
+
const TYPED = {};
|
|
264
|
+
let fieldSeq = 0;
|
|
265
|
+
|
|
266
|
+
function prompt(opts = {}) {
|
|
267
|
+
const d = doc();
|
|
268
|
+
const fid = `hall-prompt-${++fieldSeq}`;
|
|
269
|
+
const form = d.createElement('form');
|
|
270
|
+
form.className = 'hall-dialog__form';
|
|
271
|
+
form.noValidate = true;
|
|
272
|
+
const label = opts.label ? `<label class="hall-dialog__label" for="${fid}">${esc(opts.label)}</label>` : '';
|
|
273
|
+
form.innerHTML = (opts.body ? bodyHtml(opts.body) : '') + label
|
|
274
|
+
+ `<input type="text" class="hall-dialog__field" id="${fid}" autocomplete="off">`;
|
|
275
|
+
const field = form.querySelector('input');
|
|
276
|
+
if (!opts.label) field.setAttribute('aria-label', String(opts.title || ''));
|
|
277
|
+
if (opts.placeholder) field.setAttribute('placeholder', String(opts.placeholder));
|
|
278
|
+
if (opts.maxLength > 0) field.setAttribute('maxlength', String(opts.maxLength));
|
|
279
|
+
field.value = opts.value == null ? '' : String(opts.value);
|
|
280
|
+
const dlg = open({
|
|
281
|
+
title: opts.title,
|
|
282
|
+
body: form,
|
|
283
|
+
actions: [
|
|
284
|
+
{ label: opts.cancelLabel || 'Cancel', value: null, kind: 'ghost' },
|
|
285
|
+
{ label: opts.confirmLabel || 'Save', value: TYPED, kind: 'primary' },
|
|
286
|
+
],
|
|
287
|
+
trigger: opts.trigger,
|
|
288
|
+
initialFocus: () => field,
|
|
289
|
+
});
|
|
290
|
+
const ok = dlg.el.querySelector('[data-dialog-value="1"]');
|
|
291
|
+
const sync = () => { if (opts.required) ok.disabled = !field.value.trim(); };
|
|
292
|
+
sync();
|
|
293
|
+
field.addEventListener('input', sync);
|
|
294
|
+
// Enter in the one field submits the form: the same as pressing the confirm.
|
|
295
|
+
form.addEventListener('submit', (ev) => { ev.preventDefault(); if (!ok.disabled) dlg.close(TYPED); });
|
|
296
|
+
if (typeof field.select === 'function') field.select();
|
|
297
|
+
return dlg.result.then((v) => (v === TYPED ? field.value : null));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const api = { bind, open, confirm, prompt };
|
|
253
301
|
if (typeof window !== 'undefined') window.OTBDialog = api;
|
|
254
302
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
255
303
|
})();
|
|
@@ -438,13 +438,18 @@
|
|
|
438
438
|
if (v) act(() => call('PATCH', `/${v.id}`, { page: views.page, is_default: !v.is_default }),
|
|
439
439
|
v.is_default ? `“${v.name}” is no longer your default.` : `This page now opens with “${v.name}”.`);
|
|
440
440
|
} else if (t.hasAttribute('data-rename')) {
|
|
441
|
-
|
|
442
|
-
const name = v && window.prompt('New name for this view', v.name);
|
|
443
|
-
if (name && name.trim() && name.trim() !== v.name) act(() => call('PATCH', `/${v.id}`, { page: views.page, name }), 'Renamed.');
|
|
441
|
+
renameView(pick('data-rename'), t);
|
|
444
442
|
} else if (t.hasAttribute('data-delete')) {
|
|
445
443
|
deleteView(pick('data-delete'), t);
|
|
446
444
|
}
|
|
447
445
|
});
|
|
446
|
+
async function renameView(v, trigger) {
|
|
447
|
+
if (!v) return;
|
|
448
|
+
const name = await window.OTBDialog.prompt({
|
|
449
|
+
title: 'New name for this view', value: v.name, confirmLabel: 'Rename', required: true, trigger,
|
|
450
|
+
});
|
|
451
|
+
if (name && name.trim() && name.trim() !== v.name) act(() => call('PATCH', `/${v.id}`, { page: views.page, name }), 'Renamed.');
|
|
452
|
+
}
|
|
448
453
|
async function deleteView(v, trigger) {
|
|
449
454
|
if (!v || !(await window.OTBDialog.confirm({
|
|
450
455
|
title: `Delete the view “${v.name}”?`,
|
|
@@ -247,11 +247,19 @@
|
|
|
247
247
|
var btn = e.target && e.target.closest ? e.target.closest('[data-roster-status]') : null;
|
|
248
248
|
if (!btn) return;
|
|
249
249
|
var status = btn.getAttribute('data-roster-status');
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
.
|
|
250
|
+
// Offboarding asks for a reason in the hall's own dialog (task 1005136). The reason
|
|
251
|
+
// stays optional; Cancel now stops the offboarding, where the browser box's Cancel
|
|
252
|
+
// used to offboard anyway with no reason.
|
|
253
|
+
var asked = status === 'inactive'
|
|
254
|
+
? window.OTBDialog.prompt({ title: 'Reason for offboarding (optional)', confirmLabel: 'Offboard', trigger: btn })
|
|
255
|
+
: Promise.resolve('');
|
|
256
|
+
asked.then(function (reason) {
|
|
257
|
+
if (reason === null) return;
|
|
258
|
+
btn.disabled = true;
|
|
259
|
+
patchBuilder(btn.getAttribute('data-id'), 'status', { status: status, reason: reason || null })
|
|
260
|
+
.then(function () { toast(status === 'inactive' ? 'Builder offboarded.' : 'Builder reactivated.'); return refreshGov(); })
|
|
261
|
+
.catch(function (err) { btn.disabled = false; toast('Could not change standing: ' + (err.message || err)); });
|
|
262
|
+
});
|
|
255
263
|
});
|
|
256
264
|
}
|
|
257
265
|
|
|
@@ -42,7 +42,10 @@
|
|
|
42
42
|
|
|
43
43
|
async function rename(id, row) {
|
|
44
44
|
const current = row.querySelector('strong').textContent;
|
|
45
|
-
const next = window.prompt(
|
|
45
|
+
const next = await window.OTBDialog.prompt({
|
|
46
|
+
title: 'Name this card', value: current === 'Unnamed card' ? '' : current, confirmLabel: 'Save',
|
|
47
|
+
trigger: row.querySelector('.passkeys-rename'),
|
|
48
|
+
});
|
|
46
49
|
if (next === null) return;
|
|
47
50
|
try {
|
|
48
51
|
const res = await api.request('POST', `${API}/me/passkeys/rename`, { body: { credential_id: id, nickname: next } });
|
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
in the window, lighter towers with unlit panes, a warm wall and floor, the
|
|
26
26
|
lamp off and a patch of sun on the floor. The glass panels over the room
|
|
27
27
|
follow its sky; the hall chrome still keeps its own light and dark.
|
|
28
|
+
EXCEPT IN DARK (task 1005137): the day room is a light-theme room. Under the
|
|
29
|
+
hall's dark theme a cream room sat beside a black rail and bar, so every day
|
|
30
|
+
rule below is scoped html:not([data-theme="dark"]) and dark keeps the night
|
|
31
|
+
room at any hour. data-sky is only ever set by studio.js after shell.js has
|
|
32
|
+
set data-theme, so the one selector covers first paint too.
|
|
28
33
|
|
|
29
34
|
The second half is the older reviews' garden (R06 of goal 1000074, task
|
|
30
35
|
1003117), which still answers the reviews filed before Tweak Mode (TW14) in
|
|
@@ -85,8 +90,8 @@ body[data-page="studio"] {
|
|
|
85
90
|
inks are set darker than the mockup's so they hold AA on the glass over a
|
|
86
91
|
light wall (records/studio.md): the link, and the credits figure and focus
|
|
87
92
|
ring, which by night are the accent. */
|
|
88
|
-
body[data-page="ai-studio"][data-sky="day"],
|
|
89
|
-
body[data-page="studio"][data-sky="day"] {
|
|
93
|
+
html:not([data-theme="dark"]) body[data-page="ai-studio"][data-sky="day"],
|
|
94
|
+
html:not([data-theme="dark"]) body[data-page="studio"][data-sky="day"] {
|
|
90
95
|
--st-room: #efe7dc;
|
|
91
96
|
--st-frame: #d6c9b7;
|
|
92
97
|
--st-night: #bfdaf0;
|
|
@@ -132,17 +137,17 @@ body[data-page="studio"][data-sky="day"] {
|
|
|
132
137
|
}
|
|
133
138
|
/* The panels' scrim is the day wall's own tint, whatever the hall's theme, set
|
|
134
139
|
on the overlay so it outranks the theme's .42 and the phone's .88. */
|
|
135
|
-
body[data-page="studio"][data-sky="day"] .st-overlay {
|
|
140
|
+
html:not([data-theme="dark"]) body[data-page="studio"][data-sky="day"] .st-overlay {
|
|
136
141
|
--st-scrim: rgba(239, 231, 220, .9);
|
|
137
142
|
color-scheme: light;
|
|
138
143
|
}
|
|
139
|
-
body[data-page="ai-studio"][data-sky="day"] .studio,
|
|
140
|
-
body[data-page="studio"][data-sky="day"] .studio { color-scheme: light; }
|
|
144
|
+
html:not([data-theme="dark"]) body[data-page="ai-studio"][data-sky="day"] .studio,
|
|
145
|
+
html:not([data-theme="dark"]) body[data-page="studio"][data-sky="day"] .studio { color-scheme: light; }
|
|
141
146
|
/* The Approval queue (approval-queue.css) is night glass on its own root; over
|
|
142
147
|
the day room it wears the same day glass as the studio's own panels. Its
|
|
143
148
|
rendered page stays on its paper frame, and its accent button stays the
|
|
144
149
|
accent, which carries black words at AA in either sky. */
|
|
145
|
-
body[data-page="studio"][data-sky="day"] .aq-overlay {
|
|
150
|
+
html:not([data-theme="dark"]) body[data-page="studio"][data-sky="day"] .aq-overlay {
|
|
146
151
|
color-scheme: light;
|
|
147
152
|
--aq-scrim: rgba(239, 231, 220, .9);
|
|
148
153
|
--aq-glass: rgba(255, 255, 255, .75);
|
|
@@ -159,7 +164,7 @@ body[data-page="studio"][data-sky="day"] .aq-overlay {
|
|
|
159
164
|
--aq-link: var(--accent-deep);
|
|
160
165
|
--aq-rule: rgba(60, 40, 20, .12);
|
|
161
166
|
}
|
|
162
|
-
body[data-page="studio"][data-sky="day"] .aq-overlay :focus-visible { outline-color: var(--st-focus); }
|
|
167
|
+
html:not([data-theme="dark"]) body[data-page="studio"][data-sky="day"] .aq-overlay :focus-visible { outline-color: var(--st-focus); }
|
|
163
168
|
|
|
164
169
|
/* The stage fills the content box: the hall's content column is a reading
|
|
165
170
|
width, and the artboard is the whole window. */
|
|
@@ -288,11 +293,11 @@ html[data-fullscreen="on"] body[data-page="studio"] .fullscreen-toggle {
|
|
|
288
293
|
|
|
289
294
|
/* The day room: the sun rides higher and larger than the moon, the stars and
|
|
290
295
|
the lamp's glow are gone, the clouds and the patch of sun are there. */
|
|
291
|
-
body[data-sky="day"] .st-moon { left: 394px; top: 42px; width: 76px; height: 76px; box-shadow: 0 0 80px 26px var(--st-moon-halo); }
|
|
292
|
-
body[data-sky="day"] .st-star,
|
|
293
|
-
body[data-sky="day"] .st-glow { opacity: 0; }
|
|
294
|
-
body[data-sky="day"] .st-cloud { opacity: .9; }
|
|
295
|
-
body[data-sky="day"] .st-sunpatch { opacity: .7; }
|
|
296
|
+
html:not([data-theme="dark"]) body[data-sky="day"] .st-moon { left: 394px; top: 42px; width: 76px; height: 76px; box-shadow: 0 0 80px 26px var(--st-moon-halo); }
|
|
297
|
+
html:not([data-theme="dark"]) body[data-sky="day"] .st-star,
|
|
298
|
+
html:not([data-theme="dark"]) body[data-sky="day"] .st-glow { opacity: 0; }
|
|
299
|
+
html:not([data-theme="dark"]) body[data-sky="day"] .st-cloud { opacity: .9; }
|
|
300
|
+
html:not([data-theme="dark"]) body[data-sky="day"] .st-sunpatch { opacity: .7; }
|
|
296
301
|
|
|
297
302
|
/* The room's only motion: the clouds drift, and a change of sky fades over
|
|
298
303
|
.8s. Both live only without a reduced-motion preference, and style.css's
|
|
@@ -312,16 +317,10 @@ body[data-sky="day"] .st-sunpatch { opacity: .7; }
|
|
|
312
317
|
.st-actions { position: relative; z-index: 1; }
|
|
313
318
|
|
|
314
319
|
/* ---------------------------------------------------------------------------
|
|
315
|
-
The top row: the room's name,
|
|
320
|
+
The top row: the tally pill. (The room's name, a mono eyebrow above the
|
|
321
|
+
greeting, went in task 1005137: DESIGN.md bans a label block over the h1.)
|
|
316
322
|
--------------------------------------------------------------------------- */
|
|
317
323
|
.st-top { display: flex; align-items: center; min-height: 48px; gap: 16px; flex-wrap: wrap; }
|
|
318
|
-
.st-eyebrow {
|
|
319
|
-
font-family: var(--st-mono);
|
|
320
|
-
font-size: 11px;
|
|
321
|
-
letter-spacing: .14em;
|
|
322
|
-
text-transform: uppercase;
|
|
323
|
-
color: var(--st-ink-faint);
|
|
324
|
-
}
|
|
325
324
|
.st-tally {
|
|
326
325
|
margin-left: auto;
|
|
327
326
|
display: flex;
|
|
@@ -80,11 +80,10 @@
|
|
|
80
80
|
<div class="st-leaf st-leaf--2"></div>
|
|
81
81
|
</div>
|
|
82
82
|
|
|
83
|
-
<!-- The
|
|
83
|
+
<!-- The artist's tally (GET /copy-desk/tally)
|
|
84
84
|
in a glass pill top right. The shell's full-screen button keeps the
|
|
85
85
|
window's corner above it. -->
|
|
86
86
|
<div class="st-top">
|
|
87
|
-
<span class="st-eyebrow">{{worldName}} · studio</span>
|
|
88
87
|
<div class="st-tally" id="st-tally" hidden>
|
|
89
88
|
<span class="st-tally__item"><span class="st-tally__n st-tally__n--credits" id="st-tally-credits">0</span><span class="st-tally__label">credits</span></span>
|
|
90
89
|
<span class="st-tally__rule" aria-hidden="true"></span>
|
|
@@ -856,8 +856,12 @@
|
|
|
856
856
|
// round goes back to /tweak if it still holds its batch, else it is closed.
|
|
857
857
|
async function releasePage(pageId, taskId, button) {
|
|
858
858
|
if (!lib || !lib.PAGE_ID_RE.test(String(pageId || ''))) return;
|
|
859
|
-
const note = window.prompt(
|
|
860
|
-
'
|
|
859
|
+
const note = await window.OTBDialog.prompt({
|
|
860
|
+
title: 'Why is this round being released?',
|
|
861
|
+
label: 'One line, for whoever picks the page up next.',
|
|
862
|
+
value: 'Stuck in a state the Studio could not read; released so the page can be tweaked again.',
|
|
863
|
+
confirmLabel: 'Release', required: true, trigger: button,
|
|
864
|
+
});
|
|
861
865
|
if (note == null) return;
|
|
862
866
|
if (button) button.disabled = true;
|
|
863
867
|
const body = { note };
|
|
@@ -1307,7 +1307,7 @@ html[data-fullscreen="on"][data-fullscreen-plain="on"] .app-content { padding-to
|
|
|
1307
1307
|
display: inline-flex;
|
|
1308
1308
|
align-items: center;
|
|
1309
1309
|
gap: 2px;
|
|
1310
|
-
padding:
|
|
1310
|
+
padding: 2px;
|
|
1311
1311
|
border-radius: var(--radius-pill);
|
|
1312
1312
|
box-shadow: inset 0 0 0 1px var(--glass-edge);
|
|
1313
1313
|
flex-shrink: 0;
|
|
@@ -1959,6 +1959,15 @@ html.pb-editing main { padding-bottom: 180px; }
|
|
|
1959
1959
|
.topbar-trail .topbar-back svg { display: block; }
|
|
1960
1960
|
.topbar-trail .topbar-back { padding: 0 8px; }
|
|
1961
1961
|
.topbar-trail ~ .topbar-title { display: block; }
|
|
1962
|
+
/* The 320 top bar (task 1005137): the menu, the Update pill, the search, the
|
|
1963
|
+
mode switch and the three toggles measured 340px in a 320px bar, so the
|
|
1964
|
+
theme and full-screen icons were cut off at the edge. Here the bar packs
|
|
1965
|
+
tighter and each icon button is 28px — still above the 24px floor — so the
|
|
1966
|
+
whole row fits with room to spare. Pinned in tests/hall_topbar_320.mjs. */
|
|
1967
|
+
.app-topbar { gap: var(--ds-space-hair); padding: 0 var(--ds-space-xs); }
|
|
1968
|
+
.topbar-menu, .theme-toggle, .fullscreen-toggle { width: 28px; height: 28px; }
|
|
1969
|
+
.zoom-toggle { min-width: 28px; height: 28px; padding: 0 6px; }
|
|
1970
|
+
.topbar-side .topbar-jump { min-width: 28px; padding: 0 6px; }
|
|
1962
1971
|
}
|
|
1963
1972
|
.zoom-toggle__pct {
|
|
1964
1973
|
font-size: 12px;
|
|
@@ -4335,15 +4344,18 @@ a.stat-tile:hover .stat-tile__sub { color: var(--ink-soft); }
|
|
|
4335
4344
|
no new theme vars, so dark mode follows the neutral flip for free. The overlay
|
|
4336
4345
|
mirrors .deeplink-card's posture (fixed scrim + centred panel, z above the
|
|
4337
4346
|
drawer) so the two modal surfaces read as one system. */
|
|
4347
|
+
/* One shape with its neighbour (task 1005137): the search slot and the mode
|
|
4348
|
+
switch beside it are both a 32px pill with the same glass hairline, where the
|
|
4349
|
+
slot used to be a squarer, filled box next to a round one. */
|
|
4338
4350
|
.topbar-jump {
|
|
4339
4351
|
display: inline-flex;
|
|
4340
4352
|
align-items: center;
|
|
4341
4353
|
gap: 8px;
|
|
4342
4354
|
height: 32px;
|
|
4343
|
-
padding: 0 10px;
|
|
4344
|
-
border: 1px solid var(--
|
|
4345
|
-
border-radius: var(--radius-
|
|
4346
|
-
background:
|
|
4355
|
+
padding: 0 12px 0 10px;
|
|
4356
|
+
border: 1px solid var(--glass-edge);
|
|
4357
|
+
border-radius: var(--radius-pill);
|
|
4358
|
+
background: none;
|
|
4347
4359
|
color: var(--ink-faint);
|
|
4348
4360
|
font: inherit;
|
|
4349
4361
|
font-size: 13px;
|
|
@@ -109,6 +109,21 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
109
109
|
|
|
110
110
|
.top { position: fixed; left: 0; right: 0; top: 0; padding: calc(16px + env(safe-area-inset-top, 0px)) 20px 0; display: flex; align-items: flex-start; gap: 16px; pointer-events: none; z-index: 5; }
|
|
111
111
|
.top > * { pointer-events: auto; }
|
|
112
|
+
/* The sky is pannable, so its marks will always pass under the header strip and
|
|
113
|
+
the legend. Each band stands on a veil of the void that fades into the sky
|
|
114
|
+
(task 1005137: dots painted over the counts, the what-if pill and the legend).
|
|
115
|
+
The veil sits inside the band's own stacking layer, under its words and above
|
|
116
|
+
the canvas, and never catches the pointer. */
|
|
117
|
+
.top::before, .foot::before { content: ""; position: absolute; left: 0; right: 0; z-index: -1; pointer-events: none; }
|
|
118
|
+
.top::before { top: 0; bottom: -24px; background: linear-gradient(to bottom, var(--void) 62%, transparent); }
|
|
119
|
+
.foot::before { bottom: 0; top: -24px; background: linear-gradient(to top, var(--void) 62%, transparent); }
|
|
120
|
+
/* the ground the sky's own words stand on: a glow of the void itself, so a dot
|
|
121
|
+
passing under a count or a legend word dims instead of painting over it — in
|
|
122
|
+
both modes, since the void is the mode's ground */
|
|
123
|
+
.top, .foot, .composer { --sky-halo: 0 0 6px var(--void), 0 0 14px var(--void), 0 0 2px var(--void); }
|
|
124
|
+
.stats, .gravity, .obj, .alt { text-shadow: var(--sky-halo); }
|
|
125
|
+
/* the composer's hint floats lower than the strip's veil, so it carries its own */
|
|
126
|
+
.gravity:not(:empty) { padding: 2px 12px; border-radius: var(--ds-radius-tile); background: color-mix(in srgb, var(--void) 78%, transparent); }
|
|
112
127
|
.brand { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
113
128
|
.brand h1 { margin: 0; font-size: 26px; font-weight: 300; letter-spacing: -.035em; line-height: 1.05; white-space: nowrap; }
|
|
114
129
|
.brand h1 em { font-style: normal; color: var(--ink-dim); }
|
|
@@ -262,7 +277,9 @@ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
|
|
|
262
277
|
.mk.settled { width: 12px; height: 12px; background: transparent; border: 1px solid color-mix(in srgb, var(--ink) 50%, transparent); box-shadow: inset 0 0 0 3px var(--void), inset 0 0 0 4px color-mix(in srgb, var(--ink) 35%, transparent), 0 0 8px color-mix(in srgb, var(--ink) 25%, transparent); }
|
|
263
278
|
.mk.collapsed { width: 9px; height: 9px; background: var(--void); border: 1px solid color-mix(in srgb, var(--ink) 30%, transparent); box-shadow: 0 0 0 2px var(--void), 0 0 0 3px color-mix(in srgb, var(--ink) 8%, transparent); }
|
|
264
279
|
.mk.unseen { width: 12px; height: 12px; background: transparent; border: 1px dashed color-mix(in srgb, var(--ink) 20%, transparent); }
|
|
265
|
-
|
|
280
|
+
/* `task` is also the hall's task-card class (style.css): its card padding blew the
|
|
281
|
+
legend's 4px dot up into a 48px blob over its own word, so it is zeroed here. */
|
|
282
|
+
.mk.task { width: 4px; height: 4px; padding: 0; border: 0; background: color-mix(in srgb, var(--ink) 60%, var(--void)); }
|
|
266
283
|
.mk.task.live { background: var(--accent); box-shadow: 0 0 6px var(--accent); }
|
|
267
284
|
.mk.born { width: 10px; height: 10px; background: transparent; border: 1px solid var(--accent); box-shadow: inset 0 0 0 2px var(--void), inset 0 0 0 5px var(--accent); }
|
|
268
285
|
.mk.route { width: 14px; height: 2px; border-radius: 1px; background: var(--chart); box-shadow: 0 0 6px var(--chart); }
|
|
@@ -34,7 +34,8 @@ Where it differs from the mockup, and why:
|
|
|
34
34
|
- **The link, the credits figure and the focus ring are #96410f by day**, not the mockup's #a3481a link and #fb9569 accent: #a3481a on Resume's tint over the wall is 4.3:1, under AA, and the accent is 1.8:1 on the wall.
|
|
35
35
|
- **The quiet inks are #5a595f (faint) and #66656b (dim, hint)** where the mockup has the one #66656b: the studio has three quiet tiers, and each holds AA on the glass and the bare wall (the hint is the lowest, 4.7:1).
|
|
36
36
|
- **The sun, clouds and sun patch are placed for the studio's 560px window** (the mockup's is 520px): the sun at 394/42 and 76px, the clouds about 8% larger, the patch 20px right to sit under the studio's lamp.
|
|
37
|
-
- **The
|
|
37
|
+
- **The day room is a light-theme room (task 1005137).** Under the hall's dark theme a cream room sat beside the black rail and bar, so every day rule is scoped `html:not([data-theme="dark"])`: in dark the room is the night room at any hour, and the clock only changes the greeting. The room's mono eyebrow ("{{worldName}} · studio") went in the same task, since DESIGN.md bans a label above the h1, and the Approval queue took the shared dialog's corner and hairline.
|
|
38
|
+
- **The panels follow the sky**: by day the two glass panels and the Approval queue wear white glass (.75) over a scrim of the wall's own tint (.9) on a phone and in the light theme. The queue's rendered page stays on its paper frame. The mockup draws no panel.
|
|
38
39
|
- **"Good morning." while being typed over shows the page's greeting line**, "Good evening.", the one line a tweak can rewrite. Rewritten, it is the greeting at every hour.
|
|
39
40
|
- **On a phone the sun patch lies on the phone's floor** (bottom right, 300 by 70); the mockup hides it under 900px, but the studio's phone keeps its desk.
|
|
40
41
|
- **The stars do not twinkle and the lamp does not breathe at night**: the night room stays as it shipped. This task adds only the day room's drift.
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
sheets; this lays them out in sections and stops the live toast and the
|
|
4
4
|
dialog from floating, so each sits at rest where it can be looked at. */
|
|
5
5
|
body[data-page="components"] { margin: 0; color: var(--ink); background: var(--bg); font-family: var(--font-body); }
|
|
6
|
+
/* The hall layering (task 1005137): style.css paints the plate and its veil as
|
|
7
|
+
fixed body::before / body::after at z-index 0, and a hall page lifts its
|
|
8
|
+
content above them (.app-body is position: relative; z-index: 1). The
|
|
9
|
+
specimen has no .app-body, so in dark the veil (--plate-veil, black at .82-.93)
|
|
10
|
+
lay OVER every section and the page read as near-black. Its two blocks take
|
|
11
|
+
the same lift. Pinned in tests/hall_topbar_320.mjs. */
|
|
12
|
+
.spec-head, .spec-main { position: relative; z-index: 1; }
|
|
6
13
|
.spec-head, .spec-main { box-sizing: border-box; max-width: 980px; margin: 0 auto; padding: 0 var(--ds-space-md); }
|
|
7
14
|
.spec-head { padding-top: var(--ds-space-sm); padding-bottom: var(--ds-space-hair); }
|
|
8
15
|
.spec-kicker { display: inline-block; margin: 0 var(--ds-space-xs) 0 0; vertical-align: middle; font-family: var(--label-face); font-size: var(--label-size); font-weight: 400; letter-spacing: var(--label-space); text-transform: var(--label-case); color: var(--ink-faint); }
|
|
@@ -191,7 +191,12 @@
|
|
|
191
191
|
|
|
192
192
|
async function release(version) {
|
|
193
193
|
if (releasing) return;
|
|
194
|
-
|
|
194
|
+
// the hall's own confirm (hall-dialog.js; the deploy page loads it after the shell)
|
|
195
|
+
if (!(await window.OTBDialog.confirm({
|
|
196
|
+
title: `Release ${version} to every project?`,
|
|
197
|
+
body: `Every other project will be offered ${version} and everything merged before it. This cannot be pressed back; a newer version can be released after it.`,
|
|
198
|
+
confirmLabel: 'Release',
|
|
199
|
+
}))) return;
|
|
195
200
|
releasing = version;
|
|
196
201
|
releaseNote = null;
|
|
197
202
|
paint();
|
|
@@ -343,22 +343,25 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
|
|
|
343
343
|
.lede a{color:var(--link-ink);font-weight:700;text-decoration:none;}
|
|
344
344
|
.lede a:hover{text-decoration:underline;}
|
|
345
345
|
|
|
346
|
-
/* ── buttons ──
|
|
347
|
-
|
|
346
|
+
/* ── buttons ──
|
|
347
|
+
On the shared scales (task 1005136): the pill corner, the type step and the fast
|
|
348
|
+
duration and ease every surface spends. The modifier pairs answer to the hall's
|
|
349
|
+
names too (.btn--ghost, .btn--danger), which is what the shared dialog builds. */
|
|
350
|
+
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;cursor:pointer;font-family:var(--font-display);font-weight:700;font-size:var(--ds-text-lg);color:var(--accent-on);background:var(--accent);border:none;border-radius:var(--ds-radius-pill);padding:11px 22px;text-decoration:none;box-shadow:0 10px 22px -8px color-mix(in srgb,var(--accent) 70%,transparent);transition:transform var(--ds-dur-fast) var(--ds-ease), box-shadow var(--ds-dur-fast) var(--ds-ease), background var(--ds-dur-fast) var(--ds-ease);}
|
|
348
351
|
.btn:hover{transform:translateY(-1px);box-shadow:0 14px 28px -8px color-mix(in srgb,var(--accent) 80%,transparent);}
|
|
349
352
|
.btn:active{transform:translateY(0) scale(.98);}
|
|
350
353
|
.btn:disabled{background:color-mix(in srgb,var(--ink) 12%,var(--bg-deep));color:color-mix(in srgb,var(--ink) 40%,var(--bg-deep));box-shadow:none;cursor:not-allowed;transform:none;}
|
|
351
|
-
.btn.ghost{background:transparent;color:var(--ink-soft);box-shadow:none;border:2px solid var(--line);}
|
|
352
|
-
.btn.ghost:hover{border-color:var(--accent);color:var(--accent-deep);}
|
|
354
|
+
.btn.ghost,.btn.btn--ghost{background:transparent;color:var(--ink-soft);box-shadow:none;border:2px solid var(--line);}
|
|
355
|
+
.btn.ghost:hover,.btn.btn--ghost:hover{border-color:var(--accent);color:var(--accent-deep);}
|
|
353
356
|
/* a disabled ghost button must LOOK disabled — the same .btn:disabled-vs-.btn.ghost
|
|
354
357
|
specificity tie .btn.danger:disabled fixed below, applied to "Applied" on the
|
|
355
358
|
map's card (R10, task 1002286: it kept full-strength ghost styling and only the
|
|
356
359
|
cursor said it was inert, task 1003774). */
|
|
357
360
|
.btn.ghost:disabled{background:transparent;color:color-mix(in srgb,var(--ink) 40%,var(--bg-deep));border-color:color-mix(in srgb,var(--ink) 18%,transparent);box-shadow:none;cursor:not-allowed;transform:none;}
|
|
358
361
|
.btn.ghost:disabled:hover{border-color:color-mix(in srgb,var(--ink) 18%,transparent);color:color-mix(in srgb,var(--ink) 40%,var(--bg-deep));}
|
|
359
|
-
.btn.small{font-size:
|
|
360
|
-
.btn.danger{background:transparent;color:var(--err);box-shadow:none;border:2px solid color-mix(in srgb,var(--err) 35%,transparent);}
|
|
361
|
-
.btn.danger:hover{background:var(--err-soft);border-color:var(--err);}
|
|
362
|
+
.btn.small{font-size:var(--ds-text-sm-plus);padding:8px 16px;}
|
|
363
|
+
.btn.danger,.btn.btn--danger{background:transparent;color:var(--err);box-shadow:none;border:2px solid color-mix(in srgb,var(--err) 35%,transparent);}
|
|
364
|
+
.btn.danger:hover,.btn.btn--danger:hover{background:var(--err-soft);border-color:var(--err);}
|
|
362
365
|
/* a disabled danger button must LOOK disabled. .btn.danger is declared AFTER
|
|
363
366
|
.btn:disabled at equal specificity, so without these it keeps full-strength
|
|
364
367
|
--err and only the cursor says the control is inert — on a destructive button
|
|
@@ -1152,8 +1155,8 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1152
1155
|
The scrim is a plain rgba black in BOTH modes on purpose, so it needs no dark
|
|
1153
1156
|
override at all — a token here would have to be ink, and ink is near-white in
|
|
1154
1157
|
dark, which paints a white sheet over the page instead of dimming it. */
|
|
1155
|
-
.cardScrim{position:fixed;inset:0;z-index:
|
|
1156
|
-
padding:
|
|
1158
|
+
.cardScrim{position:fixed;inset:0;z-index:var(--ds-z-modal);display:flex;align-items:center;justify-content:center;
|
|
1159
|
+
padding:var(--ds-space-md);background:rgba(0,0,0,.55);backdrop-filter:blur(3px);}
|
|
1157
1160
|
/* THE POSTER (task 1003301; the v2 pick, direction §7): a specimen sheet in the
|
|
1158
1161
|
world, not a panel. min(560px,100%) on bg-card closed by one rule hairline,
|
|
1159
1162
|
no shadow (the world has no shadow vocabulary for UI). A flex column ordered
|
|
@@ -1166,13 +1169,13 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1166
1169
|
/ relationship, the faces, the facts as a mono hairline ledger, the doors. */
|
|
1167
1170
|
.projCard{position:relative;container-type:inline-size;display:flex;flex-direction:column;
|
|
1168
1171
|
width:min(560px,100%);max-height:calc(100vh - 48px);overflow:auto;text-align:left;
|
|
1169
|
-
background:var(--bg-card);border:
|
|
1172
|
+
background:var(--bg-card);border:var(--ds-line) solid var(--rule);border-radius:var(--ds-radius-panel);box-shadow:none;padding:0 0 26px;}
|
|
1170
1173
|
.projCard:not(.has-art){padding-top:26px;}
|
|
1171
1174
|
.projCard .cardArt{order:0;display:block;width:100%;aspect-ratio:5/2;object-fit:cover;
|
|
1172
|
-
border-radius:var(--radius) var(--radius) 0 0;}
|
|
1175
|
+
border-radius:var(--ds-radius-panel) var(--ds-radius-panel) 0 0;}
|
|
1173
1176
|
.projCard .cardX{position:absolute;top:12px;right:12px;z-index:4;display:inline-flex;align-items:center;
|
|
1174
|
-
justify-content:center;width:30px;height:30px;border:0;border-radius:var(--radius-pill);
|
|
1175
|
-
background:var(--glass-strong);color:var(--ink);cursor:pointer;font-size:
|
|
1177
|
+
justify-content:center;width:30px;height:30px;border:0;border-radius:var(--ds-radius-pill);
|
|
1178
|
+
background:var(--glass-strong);color:var(--ink);cursor:pointer;font-size:var(--ds-text-md);line-height:1;}
|
|
1176
1179
|
.projCard .cardX:hover{background:var(--glass-edge);}
|
|
1177
1180
|
/* on art the close control takes the void pill: the one legal literal is black with alpha */
|
|
1178
1181
|
.projCard.has-art .cardX{background:var(--void-pill);color:var(--void-ink);}
|
|
@@ -1740,3 +1743,23 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1740
1743
|
#view-new .inviteBox{margin-top:34px;}
|
|
1741
1744
|
#view-new .inviteBox .finePrint{margin-top:10px;}
|
|
1742
1745
|
#view-new #wizInviteLater{margin-top:14px;}
|
|
1746
|
+
|
|
1747
|
+
/* ── the shared dialog (hall-dialog.js; task 1005136) ──
|
|
1748
|
+
The hub's one question ("start another project while one is building?") is the
|
|
1749
|
+
hall's own dialog, OTBDialog.confirm, not the browser's box. Its frame is the
|
|
1750
|
+
project card's: the card ground, one hairline, the panel corner, on the shared
|
|
1751
|
+
modal layer, over the same black scrim, and like the card no shadow (the world
|
|
1752
|
+
has no shadow vocabulary for UI). Its buttons are this sheet's .btn (the
|
|
1753
|
+
--ghost/--danger names above). */
|
|
1754
|
+
.hall-dialog{z-index:var(--ds-z-modal);box-sizing:border-box;width:min(480px,calc(100vw - 2 * var(--ds-space-sm)));
|
|
1755
|
+
max-height:calc(100dvh - 2 * var(--ds-space-md));overflow-y:auto;margin:auto;padding:var(--ds-space-md);
|
|
1756
|
+
color:var(--ink);background:var(--bg-card);border:var(--ds-line) solid var(--rule);
|
|
1757
|
+
border-radius:var(--ds-radius-panel);box-shadow:none;text-align:left;}
|
|
1758
|
+
.hall-dialog::backdrop{background:rgba(0,0,0,.55);}
|
|
1759
|
+
.hall-dialog__head{display:flex;align-items:flex-start;gap:var(--ds-space-xs);margin:0 0 var(--ds-space-xs);}
|
|
1760
|
+
.hall-dialog__title{flex:1;margin:0;font-family:var(--font-display);font-size:var(--ds-text-3xl);font-weight:500;line-height:1.25;color:var(--ink);}
|
|
1761
|
+
.hall-dialog__x{flex:none;min-width:32px;min-height:32px;padding:0;font:inherit;font-size:var(--ds-text-4xl);line-height:1;
|
|
1762
|
+
color:var(--ink-soft);background:transparent;border:0;border-radius:var(--ds-radius-pill);cursor:pointer;}
|
|
1763
|
+
.hall-dialog__x:hover{color:var(--ink);}
|
|
1764
|
+
.hall-dialog__body p{margin:0 0 var(--ds-space-xs);font-size:var(--ds-text-md);line-height:1.5;color:var(--ink-soft);}
|
|
1765
|
+
.hall-dialog__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--ds-space-xs);margin-top:var(--ds-space-md);}
|
|
@@ -504,7 +504,9 @@ body{
|
|
|
504
504
|
@media (max-width:900px){
|
|
505
505
|
.topbar{height:var(--top-h);padding:0 12px;gap:10px}
|
|
506
506
|
.barLeft{gap:12px}
|
|
507
|
-
|
|
507
|
+
/* the shared step, not 12: at 320 signed out the bar was 1px wider than the room, and the
|
|
508
|
+
mark slid under the projects trigger by that pixel (task 1005136) */
|
|
509
|
+
.barRight{gap:var(--ds-space-xs)}
|
|
508
510
|
/* the wordmark folds away so the bar holds ONE row down to 320; the link keeps its name */
|
|
509
511
|
.barMark{min-width:44px;justify-content:center}
|
|
510
512
|
.mark-word{display:none}
|
|
@@ -515,6 +517,10 @@ body{
|
|
|
515
517
|
.pill{min-height:42px;font-size:13.5px}
|
|
516
518
|
.signin,.newacct{padding:0 14px}
|
|
517
519
|
.newacct{margin-right:6px}
|
|
520
|
+
/* signed in, the bar carries four doors and the mark: the second-account door folds
|
|
521
|
+
into the profile band (its bandFold twin) so the bar holds one row with no overlap
|
|
522
|
+
down to 320 (task 1005136) */
|
|
523
|
+
.barRight:has(.who) .newacct{display:none}
|
|
518
524
|
.bandIn{padding:0 12px 16px}
|
|
519
525
|
.bandRow{grid-template-columns:1fr;gap:0}
|
|
520
526
|
.bandT{font-size:20px}
|
|
@@ -749,6 +755,8 @@ composition A; landing composition v3, picked by the owner 2026-08-27. No roll,
|
|
|
749
755
|
Apex-only because the page is — /account is served only where
|
|
750
756
|
platform-identity runs, so a child instance loses the item whole. -->
|
|
751
757
|
<!--apex-only:start--><li class="bandCell"><a class="bandT" href="/invitations">Invitations</a></li><li class="bandCell"><a class="bandT" href="/connections">Connections</a></li><li class="bandCell"><a class="bandT" href="/account">Account</a></li><!--apex-only:end-->
|
|
758
|
+
<!-- the bar's second-account door, folded here on a narrow bar (task 1005136) -->
|
|
759
|
+
<li class="bandCell bandFold"><a class="bandT" href="/api/bongos/auth/web/start?return=/&repos=1&switch=1" title="Sign in with a different GitHub account">New account</a></li>
|
|
752
760
|
<!-- the honest hover target; the script upgrades the click to the POST -->
|
|
753
761
|
<li class="bandCell"><a class="bandT signout" href="/api/bongos/auth/logout">Sign out</a></li>
|
|
754
762
|
</ul>
|
|
@@ -2185,6 +2185,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2185
2185
|
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on <span data-brand-product-inline>Cloud Bongos</span>. <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">Open source</a> (<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>).<nav class="footNav" aria-label="Site"><a href="/#about">About</a><a href="/docs">Docs</a><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a><!--apex-only:start--><a data-link-hall href="{{buildersOrigin}}">Builders' hall</a><!--apex-only:end--></nav></footer>
|
|
2186
2186
|
|
|
2187
2187
|
<script src="/modules/hall-ui/public/dom-utils.js"></script>
|
|
2188
|
+
<!-- the hall's one dialog (task 1005136): the hub asks its question in it, never in the browser's box -->
|
|
2189
|
+
<script src="/modules/hall-ui/public/hall-dialog.js"></script>
|
|
2188
2190
|
<script>
|
|
2189
2191
|
/* Brand-pack fill (R57): the server injects window.__BRANDING__ ahead of this
|
|
2190
2192
|
script; baked markup above is the standalone fallback. */
|
|
@@ -7855,8 +7857,14 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7855
7857
|
var again = document.createElement('button');
|
|
7856
7858
|
again.type = 'button'; again.className = 'again'; again.textContent = 'Start another project';
|
|
7857
7859
|
again.addEventListener('click', function () {
|
|
7858
|
-
/* a standup still being watched is not abandoned by accident
|
|
7859
|
-
|
|
7860
|
+
/* a standup still being watched is not abandoned by accident — asked in the
|
|
7861
|
+
shared dialog (hall-dialog.js), never the browser's box (task 1005136) */
|
|
7862
|
+
var asked = instanceId && pollTimer
|
|
7863
|
+
? window.OTBDialog.confirm({ title: 'Start a new one?', body: (state.name || state.slug) + ' keeps building — you’ll find it on your projects page.', confirmLabel: 'Start a new one', trigger: again })
|
|
7864
|
+
: Promise.resolve(true);
|
|
7865
|
+
asked.then(function (yes) { if (yes) startAnother(); });
|
|
7866
|
+
});
|
|
7867
|
+
function startAnother() {
|
|
7860
7868
|
resetForNewProject();
|
|
7861
7869
|
/* the deliberate goodbye: the record goes too, so nothing offers to resume
|
|
7862
7870
|
the project the founder has just said they are finished watching */
|
|
@@ -7868,7 +7876,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7868
7876
|
try { sessionStorage.removeItem(CREATED_KEY); } catch (e) {}
|
|
7869
7877
|
paintResumeOffer(null);
|
|
7870
7878
|
show(1, true);
|
|
7871
|
-
}
|
|
7879
|
+
}
|
|
7872
7880
|
$('doneActs').insertBefore(again, $('hopLink'));
|
|
7873
7881
|
}
|
|
7874
7882
|
wireAgain();
|