@bongos/core 1.19.339 → 1.19.340
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 +14 -14
- package/docs/copy-inventory.md +153 -151
- package/docs/copy-registry.json +212 -194
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +18 -3
- package/public-landing/projects.html +191 -51
- package/src/module-api.js +3 -1
- package/tests/projects_hub.mjs +1 -1
- package/tests/projects_hub_pre_uat.mjs +85 -4
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.340",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.340",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.340",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
|
@@ -201,6 +201,10 @@
|
|
|
201
201
|
|
|
202
202
|
*{box-sizing:border-box;margin:0;padding:0;}
|
|
203
203
|
[hidden]{display:none !important;}
|
|
204
|
+
/* visually-hidden: said by a screen reader, never painted. The done panel's
|
|
205
|
+
progress strip needs it (its dots carry each stage's state for the eye only),
|
|
206
|
+
and the hub's map heading and skip link ride on it. */
|
|
207
|
+
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
|
|
204
208
|
html{scroll-behavior:smooth;}
|
|
205
209
|
body{font-family:var(--font-body);color:var(--ink);background:var(--page-bg);min-height:100vh;-webkit-font-smoothing:antialiased;overflow-x:hidden;}
|
|
206
210
|
|
|
@@ -283,14 +287,18 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
|
|
|
283
287
|
verification — warm when verified live, amber-dim when stale, ashen when
|
|
284
288
|
it failed its check, ghosted when torn down. Born for the dark sky; on
|
|
285
289
|
light cards the inset ring keeps it grounded. ── */
|
|
286
|
-
.orb{flex:0 0 54px;width:54px;height:54px;border-radius:50%;position:relative;overflow:visible;
|
|
290
|
+
.orb{flex:0 0 54px;width:54px;height:54px;border-radius:50%;position:relative;overflow:visible;border:none;padding:0;
|
|
287
291
|
background:radial-gradient(120% 120% at 50% 50%, var(--acc,#fb923c), color-mix(in srgb, var(--acc,#fb923c) 55%, #1b2b52) 100%);
|
|
288
292
|
box-shadow:0 0 0 1.5px rgba(255,255,255,.35) inset, 0 6px 18px rgba(20,16,40,.28), 0 0 26px var(--glow, rgba(251,146,60,.5));}
|
|
289
293
|
.orb::after{content:'';position:absolute;inset:0;border-radius:50%;pointer-events:none;
|
|
290
294
|
background:radial-gradient(38% 34% at 34% 28%, rgba(255,255,255,.9), rgba(255,255,255,0) 60%);}
|
|
291
295
|
.orb.dim{filter:saturate(.55) brightness(.92);--glow:rgba(217,119,6,.35);}
|
|
292
296
|
.orb.ashen{filter:saturate(.3) brightness(.78);--glow:rgba(220,38,38,.22);}
|
|
293
|
-
.orb.ghost{opacity:.4;box-shadow:0 0 0 1.5px rgba(255,255,255,.25) inset;filter:saturate(.2);
|
|
297
|
+
.orb.ghost{opacity:.4;box-shadow:0 0 0 1.5px rgba(255,255,255,.25) inset;filter:saturate(.2);}
|
|
298
|
+
/* only the LINKED orb is clickable: orbFor() emits a <span class="orb"> for
|
|
299
|
+
every non-navigable state, and index.html's .conItem .orb takes cursor:inherit
|
|
300
|
+
from the <a> it sits inside — so .orb.ghost's cursor reset is redundant. */
|
|
301
|
+
a.orb{cursor:pointer;}
|
|
294
302
|
|
|
295
303
|
/* ── project cards ── */
|
|
296
304
|
.projects{list-style:none;display:flex;flex-direction:column;gap:18px;}
|
|
@@ -622,6 +630,10 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
622
630
|
.tbl th{text-align:left;font-family:var(--font-display);font-size:11.5px;font-weight:700;
|
|
623
631
|
letter-spacing:.9px;text-transform:uppercase;color:var(--ink-faint);padding:15px 18px;
|
|
624
632
|
border-bottom:2px solid var(--line);white-space:nowrap;}
|
|
633
|
+
/* the sort control is a real button so the keyboard can reach it; it must look
|
|
634
|
+
like the header it replaced, so it inherits everything the th declares. */
|
|
635
|
+
.tbl th button{all:unset;cursor:pointer;}
|
|
636
|
+
.tbl th button:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
|
|
625
637
|
.tbl td{padding:15px 18px;border-bottom:1px solid var(--line);font-size:14px;font-weight:600;
|
|
626
638
|
color:var(--ink-soft);vertical-align:middle;}
|
|
627
639
|
.tbl tr:last-child td{border-bottom:none;}
|
|
@@ -631,7 +643,8 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
631
643
|
.tbl .pname{display:flex;align-items:center;gap:11px;}
|
|
632
644
|
.tbl .pdot{width:11px;height:11px;border-radius:50%;background:var(--acc);flex:0 0 auto;
|
|
633
645
|
box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 22%,transparent);}
|
|
634
|
-
.tbl .ptitle{font-family:var(--font-display);font-weight:700;font-size:15px;color:var(--ink);}
|
|
646
|
+
.tbl .ptitle{font-family:var(--font-display);font-weight:700;font-size:15px;color:var(--ink);text-decoration:none;}
|
|
647
|
+
.tbl a.ptitle:hover{text-decoration:underline;}
|
|
635
648
|
.tbl .ptag{font-size:12.5px;color:var(--ink-faint);font-weight:600;}
|
|
636
649
|
.tbl .num{font-family:var(--font-mono);font-weight:500;}
|
|
637
650
|
.tbl .tags{display:flex;gap:6px;flex-wrap:wrap;}
|
|
@@ -753,6 +766,8 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
753
766
|
.steps li.now .snum{border-color:var(--accent);color:var(--accent-deep);}
|
|
754
767
|
.steps .slbl{font-size:12.5px;font-weight:700;color:var(--ink-faint);}
|
|
755
768
|
.steps li.now .slbl,.steps li.done .slbl{color:var(--ink-soft);}
|
|
769
|
+
.steps li[role=button]{cursor:pointer;}
|
|
770
|
+
.steps li[role=button]:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:8px;}
|
|
756
771
|
.createSplit{display:grid;grid-template-columns:1fr 300px;gap:var(--pad-md);align-items:center;}
|
|
757
772
|
@media (max-width:840px){.createSplit{grid-template-columns:1fr;}.createSplit .bongoCell{display:none;}}
|
|
758
773
|
|
|
@@ -40,6 +40,11 @@ try{var _m=localStorage.getItem('cb-color-mode');if(_m==='light'||_m==='dark')do
|
|
|
40
40
|
|
|
41
41
|
Same trust boundary as /create — every button only ASKS (enqueues an
|
|
42
42
|
intent); the control-plane runner does the real infra work (ADR 0111 §2). */
|
|
43
|
+
/* the skip link un-hides ITSELF on focus (0,2,0 beats cosmos's .vh at 0,1,0):
|
|
44
|
+
every property .vh sets is reversed here, so it is a real pill on first Tab */
|
|
45
|
+
.skip:focus{position:fixed;top:8px;left:8px;z-index:100;width:auto;height:auto;margin:0;overflow:visible;clip-path:none;white-space:normal;
|
|
46
|
+
font-size:14px;font-weight:800;text-decoration:none;color:var(--ink);background:var(--bg-card);
|
|
47
|
+
border:2px solid var(--ctl-line);border-radius:12px;padding:10px 16px;}
|
|
43
48
|
.viewWrap{padding-bottom:40px;}
|
|
44
49
|
.card{margin-bottom:26px;}
|
|
45
50
|
/* page titles align with the cards below them — cosmos's .hero is 880px-centred
|
|
@@ -255,9 +260,13 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
255
260
|
.again{
|
|
256
261
|
display:inline-block;margin-top:14px;font-size:13px;font-weight:700;color:var(--ink-faint);
|
|
257
262
|
background:none;border:none;cursor:pointer;font-family:var(--font-body);text-decoration:underline;
|
|
258
|
-
text-underline-offset:3px;padding:
|
|
263
|
+
text-underline-offset:3px;padding:6px 2px;min-height:24px;
|
|
259
264
|
}
|
|
260
265
|
.again:hover{color:var(--accent-deep);}
|
|
266
|
+
/* every fold on this page is a real target: these summaries are 12.5–13px text
|
|
267
|
+
and measured under the 24px minimum (WCAG 2.2 target size, AA). Not
|
|
268
|
+
display:flex — that would drop the disclosure marker every fold shows today. */
|
|
269
|
+
summary{min-height:24px;padding:3px 0;}
|
|
261
270
|
|
|
262
271
|
/* the collapsed address step — a confirmation, not a form (task 1002704) */
|
|
263
272
|
.addrConfirm{border:2px solid color-mix(in srgb,var(--ok) 30%,var(--bg-card));border-radius:18px;padding:18px 20px;background:linear-gradient(180deg,color-mix(in srgb,var(--ok) 8%,var(--bg-card)) 0%,var(--bg-card) 100%);margin-bottom:16px;}
|
|
@@ -285,7 +294,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
285
294
|
.ghrow{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:0;border-bottom:1px solid color-mix(in srgb,var(--ink) 8%,transparent);padding:9px 12px;font:inherit;font-size:13.5px;color:var(--ink);cursor:pointer;}
|
|
286
295
|
.ghrow:last-child{border-bottom:0;}
|
|
287
296
|
.ghrow:hover{background:color-mix(in srgb,var(--accent) 6%,var(--bg-deep));}
|
|
288
|
-
.ghrow[aria-
|
|
297
|
+
.ghrow[aria-current=true]{background:color-mix(in srgb,var(--accent) 12%,var(--bg-deep));box-shadow:inset 3px 0 0 var(--accent);font-weight:600;}
|
|
289
298
|
/* --ink-soft, not --ink-faint: the badge tint is the darkest ground on the page,
|
|
290
299
|
and faint ink only reaches 3.96:1 on it (task 1002749). */
|
|
291
300
|
.ghrow .priv{margin-left:auto;font-size:11px;font-weight:600;color:var(--ink-soft);background:color-mix(in srgb,var(--ink) 8%,transparent);border-radius:5px;padding:1px 6px;}
|
|
@@ -309,6 +318,12 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
309
318
|
/* 840px matches cosmos's createSplit collapse, the breakpoint the old /create
|
|
310
319
|
used (860) — the two-up grids and the bongo cell fold together */
|
|
311
320
|
@media (max-width:840px){.row2,.choices{grid-template-columns:1fr;}}
|
|
321
|
+
|
|
322
|
+
/* cosmos's global reduced-motion kill switch zeroes every animation, which
|
|
323
|
+
leaves the mark's blinking caret parked at FULL opacity — a solid bar beside
|
|
324
|
+
the wordmark forever. Page-local on purpose: a second reduce block in
|
|
325
|
+
cosmos.css would shadow the first one tests/landing_page.mjs extracts. */
|
|
326
|
+
@media (prefers-reduced-motion:reduce){.shell .caret{display:none;}}
|
|
312
327
|
</style>
|
|
313
328
|
</head>
|
|
314
329
|
<body>
|
|
@@ -340,6 +355,10 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
340
355
|
page. They sit in one control because they are all "where can I go from
|
|
341
356
|
here", but the divider keeps the distinction honest — Home navigates and
|
|
342
357
|
never takes the current-view pill. ── -->
|
|
358
|
+
<!-- keyboard skip: the chrome above every view is a wordmark, a four-item nav
|
|
359
|
+
and a right-hand cluster. Hidden until focused (.vh), it becomes a real
|
|
360
|
+
pill on first Tab and jumps to the #hubMain landmark. -->
|
|
361
|
+
<a class="skip vh" href="#hubMain">Skip to content</a>
|
|
343
362
|
<header class="shell">
|
|
344
363
|
<!-- The mark types the PREFIX and holds `bongos` still, because `bongos` is
|
|
345
364
|
the word you type and the assistant is what varies — which is also what
|
|
@@ -383,7 +402,9 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
383
402
|
What you land on, signed in or out (owner, 2026-08-13). The orbs are
|
|
384
403
|
placed statically on purpose: where things sit is this pass; how the sky
|
|
385
404
|
moves is not. -->
|
|
405
|
+
<main id="hubMain">
|
|
386
406
|
<section id="view-map">
|
|
407
|
+
<h1 class="vh">The project map</h1>
|
|
387
408
|
<div class="toolbar">
|
|
388
409
|
<div class="seg" role="group" aria-label="Map display">
|
|
389
410
|
<button type="button" id="sub-sky" aria-pressed="true">Sky</button>
|
|
@@ -441,9 +462,9 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
441
462
|
<table class="tbl">
|
|
442
463
|
<thead>
|
|
443
464
|
<tr>
|
|
444
|
-
<th class="colName" data-sort="name" aria-sort="ascending">Project</th>
|
|
465
|
+
<th class="colName" data-sort="name" aria-sort="ascending"><button type="button" class="thSort">Project</button></th>
|
|
445
466
|
<th class="colType">Type</th>
|
|
446
|
-
<th class="right" data-sort="builders">Builders</th>
|
|
467
|
+
<th class="right" data-sort="builders"><button type="button" class="thSort">Builders</button></th>
|
|
447
468
|
<th class="colTags">Runs</th>
|
|
448
469
|
<th class="right colStatus">Status</th>
|
|
449
470
|
<th class="colGo"></th>
|
|
@@ -468,7 +489,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
468
489
|
<!--apex-only:end-->
|
|
469
490
|
</tbody>
|
|
470
491
|
</table>
|
|
471
|
-
<p class="finePrint" style="text-align:center">
|
|
492
|
+
<p class="finePrint" style="text-align:center">Sort by Project or Builders.</p>
|
|
472
493
|
</div>
|
|
473
494
|
|
|
474
495
|
<!-- The project card. An OVERLAY inside the map, not a fourth view: opening
|
|
@@ -519,7 +540,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
519
540
|
Each card says what the platform <b>last verified</b>, and when.</p>
|
|
520
541
|
</div>
|
|
521
542
|
|
|
522
|
-
<
|
|
543
|
+
<div class="card">
|
|
523
544
|
<!-- signed-out gate: identity-only sign-in (ADR 0143 least-privilege — unlike
|
|
524
545
|
/create, listing your own instances needs no repo scope) -->
|
|
525
546
|
<section id="gate" hidden>
|
|
@@ -546,11 +567,11 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
546
567
|
<span class="count" id="mineCount"></span>
|
|
547
568
|
</div>
|
|
548
569
|
<div class="filters" role="group" aria-label="Filter by state">
|
|
549
|
-
<button type="button" data-f="all" aria-pressed="true">All <span class="n">0</span></button>
|
|
550
|
-
<button type="button" data-f="live" aria-pressed="false">Live <span class="n">0</span></button>
|
|
551
|
-
<button type="button" data-f="attention" aria-pressed="false">Needs you <span class="n">0</span></button>
|
|
552
|
-
<button type="button" data-f="working" aria-pressed="false">Working <span class="n">0</span></button>
|
|
553
|
-
<button type="button" data-f="gone" aria-pressed="false">Torn down <span class="n">0</span></button>
|
|
570
|
+
<button type="button" data-f="all" aria-controls="projectsList" aria-pressed="true">All <span class="n">0</span></button>
|
|
571
|
+
<button type="button" data-f="live" aria-controls="projectsList" aria-pressed="false">Live <span class="n">0</span></button>
|
|
572
|
+
<button type="button" data-f="attention" aria-controls="projectsList" aria-pressed="false">Needs you <span class="n">0</span></button>
|
|
573
|
+
<button type="button" data-f="working" aria-controls="projectsList" aria-pressed="false">Working <span class="n">0</span></button>
|
|
574
|
+
<button type="button" data-f="gone" aria-controls="projectsList" aria-pressed="false">Torn down <span class="n">0</span></button>
|
|
554
575
|
</div>
|
|
555
576
|
</div>
|
|
556
577
|
<p class="mineEmpty" id="mineEmpty" hidden>Nothing matches that. <button type="button" class="linklike" id="mineClear">Clear the filters</button></p>
|
|
@@ -562,7 +583,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
562
583
|
<ul class="projects" id="projectsList"></ul>
|
|
563
584
|
<p class="finePrint">Buttons here ask the platform to act; each card reports only what was verified, and when. A standup keeps going even with this tab closed.</p>
|
|
564
585
|
</section>
|
|
565
|
-
</
|
|
586
|
+
</div>
|
|
566
587
|
</div>
|
|
567
588
|
</section>
|
|
568
589
|
|
|
@@ -802,7 +823,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
802
823
|
<div id="ghAdopt" class="wizField" hidden>
|
|
803
824
|
<span>Pick one of your repositories</span>
|
|
804
825
|
<input type="text" id="ghSearch" placeholder="Search your repos…" autocomplete="off" aria-label="Search your repositories">
|
|
805
|
-
<div id="ghList" class="ghlist" role="
|
|
826
|
+
<div id="ghList" class="ghlist" role="group" aria-label="Your repositories"></div>
|
|
806
827
|
<div class="hint" id="ghAdoptHint"> </div>
|
|
807
828
|
<!-- visibility stated on EVERY repo path (task 1002721) — the picker
|
|
808
829
|
marks private repos, and this says what adoption does about it -->
|
|
@@ -896,7 +917,9 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
896
917
|
<h1 id="done-h">The drums are rolling</h1>
|
|
897
918
|
</div>
|
|
898
919
|
<p class="lede" id="doneLede"></p>
|
|
899
|
-
|
|
920
|
+
<!-- the STABLE live region: the strip inside is rebuilt every 5s tick,
|
|
921
|
+
and a live region that is itself replaced never announces -->
|
|
922
|
+
<div id="liveProgress" role="status" aria-live="polite"></div>
|
|
900
923
|
<h2 style="font-family:var(--font-display);font-size:19px;margin-top:22px;">Your steps — the parts only you can play</h2>
|
|
901
924
|
<ol class="humanSteps" id="humanSteps"></ol>
|
|
902
925
|
<p class="samePath">These are the same steps however a project is created — nothing here is special to this page.</p>
|
|
@@ -977,7 +1000,9 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
977
1000
|
</div>
|
|
978
1001
|
</section>
|
|
979
1002
|
|
|
980
|
-
|
|
1003
|
+
</main>
|
|
1004
|
+
|
|
1005
|
+
<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.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.</footer>
|
|
981
1006
|
|
|
982
1007
|
<script>
|
|
983
1008
|
/* Brand-pack fill (R57): the server injects window.__BRANDING__ ahead of this
|
|
@@ -1008,7 +1033,15 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1008
1033
|
if (m === 'light' || m === 'dark') return m;
|
|
1009
1034
|
return (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light';
|
|
1010
1035
|
}
|
|
1011
|
-
|
|
1036
|
+
/* the glyph shows the mode you would GET, so the label must say the same:
|
|
1037
|
+
a bare ☀/☾ announces as "sun"/"moon" and nothing about what the press does.
|
|
1038
|
+
No aria-pressed — this swaps a mode, it is not a two-state toggle button. */
|
|
1039
|
+
function paintModeToggle() {
|
|
1040
|
+
var dark = resolvedMode() === 'dark';
|
|
1041
|
+
var el = $('modeToggle');
|
|
1042
|
+
el.textContent = dark ? '☀' : '☾';
|
|
1043
|
+
el.setAttribute('aria-label', dark ? 'Switch to light mode' : 'Switch to dark mode');
|
|
1044
|
+
}
|
|
1012
1045
|
$('modeToggle').addEventListener('click', function () {
|
|
1013
1046
|
var next = resolvedMode() === 'dark' ? 'light' : 'dark';
|
|
1014
1047
|
document.documentElement.setAttribute('data-mode', next);
|
|
@@ -1022,7 +1055,20 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1022
1055
|
});
|
|
1023
1056
|
}
|
|
1024
1057
|
|
|
1025
|
-
|
|
1058
|
+
/* HELD, not sampled once: a visitor who flips "reduce motion" mid-session must
|
|
1059
|
+
stop the sky then and there (and get it back if they flip it off), instead
|
|
1060
|
+
of being stuck with whatever the preference was at load. addListener is the
|
|
1061
|
+
Safari <14 fallback — MediaQueryList only grew addEventListener later. */
|
|
1062
|
+
var REDUCED_MQ = window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)') : null;
|
|
1063
|
+
var REDUCED = !!(REDUCED_MQ && REDUCED_MQ.matches);
|
|
1064
|
+
if (REDUCED_MQ) {
|
|
1065
|
+
var onReducedChange = function (ev) {
|
|
1066
|
+
REDUCED = !!(ev && typeof ev.matches === 'boolean' ? ev.matches : REDUCED_MQ.matches);
|
|
1067
|
+
if (REDUCED) stopSky(); else startSky();
|
|
1068
|
+
};
|
|
1069
|
+
if (REDUCED_MQ.addEventListener) REDUCED_MQ.addEventListener('change', onReducedChange);
|
|
1070
|
+
else if (REDUCED_MQ.addListener) REDUCED_MQ.addListener(onReducedChange);
|
|
1071
|
+
}
|
|
1026
1072
|
|
|
1027
1073
|
/* ══════════════════════════════════════════════════════════════════════════
|
|
1028
1074
|
THE HUB SHELL — one surface, three views, one real URL.
|
|
@@ -1111,7 +1157,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1111
1157
|
});
|
|
1112
1158
|
/* Back / Forward land on the view the URL names — viewFromUrl() reads the
|
|
1113
1159
|
manage id too, so a manage entry restores its project */
|
|
1114
|
-
window.addEventListener('popstate', function () { setView(viewFromUrl()); });
|
|
1160
|
+
window.addEventListener('popstate', function () { closeCard(); setView(viewFromUrl()); });
|
|
1115
1161
|
|
|
1116
1162
|
/* ── the self-typing mark. `bongos` is the CONSTANT — it is the word you
|
|
1117
1163
|
type — and the prefix is what varies, which is exactly what the vanity
|
|
@@ -1124,6 +1170,11 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1124
1170
|
var prefix = parts.join(' ');
|
|
1125
1171
|
$('markWord').textContent = word;
|
|
1126
1172
|
$('markPrefix').textContent = prefix;
|
|
1173
|
+
/* the link's name is its two spans, so mid-type it announces "gem bongos".
|
|
1174
|
+
Label the LINK instead — before the early return, so a re-branded or
|
|
1175
|
+
reduced-motion visitor gets it too. #markLeft stays visible to AT:
|
|
1176
|
+
tests/projects_hub.mjs pins that markup literal. */
|
|
1177
|
+
$('markLeft').parentNode.setAttribute('aria-label', productName + ' — home');
|
|
1127
1178
|
if (REDUCED || prefix !== 'cloud' || word !== 'bongos') return;
|
|
1128
1179
|
var PREFIXES = ['cloud', 'claude', 'chatgpt', 'gemini'];
|
|
1129
1180
|
|
|
@@ -1367,7 +1418,9 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1367
1418
|
'data-origin="' + esc(String(p.origin || '')) + '">' +
|
|
1368
1419
|
'<td class="colName"><span class="pname"><span class="pdot" style="--acc:' +
|
|
1369
1420
|
accentOf(p) + '"></span>' +
|
|
1370
|
-
'<span
|
|
1421
|
+
'<span>' + (p._inst
|
|
1422
|
+
? '<a class="ptitle" data-manage="' + esc(String(p._inst.id)) + '" href="?view=manage&id=' + esc(String(p._inst.id)) + '">' + esc(p.name) + '</a>'
|
|
1423
|
+
: '<span class="ptitle">' + esc(p.name) + '</span>') + pill + '<br>' +
|
|
1371
1424
|
'<span class="ptag">' + esc(p.tagline || '') + '</span></span></span></td>' +
|
|
1372
1425
|
/* Builders and Runs come from the project's OWN public API, enriched
|
|
1373
1426
|
server-side (task 1002771). null is unknown — the project was
|
|
@@ -1561,12 +1614,24 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1561
1614
|
: '';
|
|
1562
1615
|
cardReturn = returnTo || null;
|
|
1563
1616
|
$('cardScrim').hidden = false;
|
|
1617
|
+
pageInert(true);
|
|
1564
1618
|
$('cardClose').focus();
|
|
1565
1619
|
}
|
|
1566
1620
|
|
|
1621
|
+
/* While the dialog is open the page behind it is inert (task 1003246): no tab
|
|
1622
|
+
stop, no screen-reader route out into the map. aria-modal alone only tells
|
|
1623
|
+
AT; it does not stop Tab. The card is the MAP's own child, so the map is
|
|
1624
|
+
inerted a child at a time — inert on an ancestor can never be lifted on a
|
|
1625
|
+
descendant, and inerting #view-map would inert the card with it. */
|
|
1626
|
+
function pageInert(on) {
|
|
1627
|
+
document.querySelectorAll('a.skip, header.shell, footer.foot, #view-mine, #view-new, #view-manage, #view-map > :not(#cardScrim)')
|
|
1628
|
+
.forEach(function (el) { if (on) el.setAttribute('inert', ''); else el.removeAttribute('inert'); });
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1567
1631
|
function closeCard() {
|
|
1568
1632
|
if ($('cardScrim').hidden) return;
|
|
1569
1633
|
$('cardScrim').hidden = true;
|
|
1634
|
+
pageInert(false); /* before the handback — an inert element cannot take focus */
|
|
1570
1635
|
if (cardReturn && cardReturn.focus) cardReturn.focus();
|
|
1571
1636
|
cardReturn = null;
|
|
1572
1637
|
}
|
|
@@ -1611,6 +1676,14 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1611
1676
|
}
|
|
1612
1677
|
|
|
1613
1678
|
$('mapRows').addEventListener('click', function (ev) {
|
|
1679
|
+
/* an owned row's title is a real manage anchor — a modified click keeps its
|
|
1680
|
+
browser meaning, a plain one stays in-page (the orb-click contract) */
|
|
1681
|
+
var m = ev.target.closest && ev.target.closest('a[data-manage]');
|
|
1682
|
+
if (m) {
|
|
1683
|
+
if (!isPlainClick(ev)) return;
|
|
1684
|
+
ev.preventDefault();
|
|
1685
|
+
return goToManage(m.getAttribute('data-manage'));
|
|
1686
|
+
}
|
|
1614
1687
|
/* the row's own "Open →" keeps its job — the card is for the rest of the row */
|
|
1615
1688
|
if (ev.target.closest && ev.target.closest('a')) return;
|
|
1616
1689
|
var tr = ev.target.closest && ev.target.closest('tr');
|
|
@@ -1711,9 +1784,13 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
1711
1784
|
|
|
1712
1785
|
$('onlyMine').addEventListener('change', applyMapFilter);
|
|
1713
1786
|
$('mapQ').addEventListener('input', applyMapFilter);
|
|
1787
|
+
/* the sort control is the BUTTON, not the th: a th takes no focus and no
|
|
1788
|
+
Enter/Space, so the column sort was mouse-only. aria-sort stays on the th,
|
|
1789
|
+
which is where a screen reader reads it from. */
|
|
1714
1790
|
document.querySelectorAll('.tbl th[data-sort]').forEach(function (th) {
|
|
1715
|
-
|
|
1716
|
-
|
|
1791
|
+
var btn = th.querySelector('button');
|
|
1792
|
+
if (!btn) return;
|
|
1793
|
+
btn.addEventListener('click', function () {
|
|
1717
1794
|
var k = th.getAttribute('data-sort');
|
|
1718
1795
|
mapSort.dir = (mapSort.key === k) ? -mapSort.dir : 1;
|
|
1719
1796
|
mapSort.key = k;
|
|
@@ -2413,6 +2490,19 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
2413
2490
|
li.classList.toggle('now', now);
|
|
2414
2491
|
li.classList.toggle('done', landed || n < cur);
|
|
2415
2492
|
if (now) li.setAttribute('aria-current', 'step'); else li.removeAttribute('aria-current');
|
|
2493
|
+
/* a step already answered is a way BACK (task 1003246): only those take
|
|
2494
|
+
the button role, the tab stop and the data-goto the delegated handler
|
|
2495
|
+
below reads. The done panel offers none — the project is filed, and
|
|
2496
|
+
there is nothing behind it to change. */
|
|
2497
|
+
if (step < 6 && n < cur) {
|
|
2498
|
+
li.setAttribute('role', 'button');
|
|
2499
|
+
li.setAttribute('tabindex', '0');
|
|
2500
|
+
li.setAttribute('data-goto', String(n));
|
|
2501
|
+
} else {
|
|
2502
|
+
li.removeAttribute('role');
|
|
2503
|
+
li.removeAttribute('tabindex');
|
|
2504
|
+
li.removeAttribute('data-goto');
|
|
2505
|
+
}
|
|
2416
2506
|
});
|
|
2417
2507
|
}
|
|
2418
2508
|
function show(step, moveFocus) {
|
|
@@ -2444,6 +2534,24 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
2444
2534
|
if (step === 5) renderReview();
|
|
2445
2535
|
}
|
|
2446
2536
|
|
|
2537
|
+
/* One delegated pair on the rail, so paintRail can add and drop data-goto
|
|
2538
|
+
freely (task 1003246). Enter and Space are the contract role="button"
|
|
2539
|
+
makes; Space must not scroll the card while it keeps it. */
|
|
2540
|
+
var railEl = root.querySelector('.steps');
|
|
2541
|
+
if (railEl) {
|
|
2542
|
+
railEl.addEventListener('click', function (ev) {
|
|
2543
|
+
var li = ev.target.closest && ev.target.closest('li[data-goto]');
|
|
2544
|
+
if (li) show(Number(li.getAttribute('data-goto')), true);
|
|
2545
|
+
});
|
|
2546
|
+
railEl.addEventListener('keydown', function (ev) {
|
|
2547
|
+
if (ev.key !== 'Enter' && ev.key !== ' ' && ev.key !== 'Spacebar') return;
|
|
2548
|
+
var li = ev.target.closest && ev.target.closest('li[data-goto]');
|
|
2549
|
+
if (!li) return;
|
|
2550
|
+
ev.preventDefault();
|
|
2551
|
+
show(Number(li.getAttribute('data-goto')), true);
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2447
2555
|
/* ── field wiring ── */
|
|
2448
2556
|
$('newName').value = state.name;
|
|
2449
2557
|
$('f-owner').value = state.owner;
|
|
@@ -2601,7 +2709,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
2601
2709
|
var rows = repoList.filter(function (r) { return !effectiveQ || String(r.full_name).toLowerCase().indexOf(effectiveQ) >= 0; }).slice(0, 100);
|
|
2602
2710
|
$('ghList').innerHTML = rows.length
|
|
2603
2711
|
? rows.map(function (r) {
|
|
2604
|
-
return '<button type="button" class="ghrow"
|
|
2712
|
+
return '<button type="button" class="ghrow" data-full="' + esc(r.full_name) + '" aria-current="' + (r.full_name === sel) + '">' +
|
|
2605
2713
|
'<span>' + esc(r.full_name) + '</span>' + (r.private ? '<span class="priv">Private</span>' : '') + '</button>';
|
|
2606
2714
|
}).join('')
|
|
2607
2715
|
: '<div class="hint" style="padding:10px 12px">No repositories match “' + esc(effectiveQ) + '”.</div>';
|
|
@@ -2628,6 +2736,20 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
2628
2736
|
}
|
|
2629
2737
|
$('ghSearch').addEventListener('focus', function () { this.select(); openRepoList(); });
|
|
2630
2738
|
$('ghSearch').addEventListener('input', openRepoList);
|
|
2739
|
+
/* A dropdown must be dismissable and must not outlive the field it belongs
|
|
2740
|
+
to (task 1003246). Escape hands focus back to the box FIRST, because the
|
|
2741
|
+
box's own focus handler reopens the list — the collapse has to be the
|
|
2742
|
+
last word. A null relatedTarget is left alone: some browsers report it
|
|
2743
|
+
for a click on a row, and closing there would eat the pick. */
|
|
2744
|
+
$('ghAdopt').addEventListener('keydown', function (ev) {
|
|
2745
|
+
if (ev.key !== 'Escape' || $('ghList').hidden) return;
|
|
2746
|
+
ev.stopPropagation();
|
|
2747
|
+
$('ghSearch').focus();
|
|
2748
|
+
closeRepoList();
|
|
2749
|
+
});
|
|
2750
|
+
$('ghAdopt').addEventListener('focusout', function (ev) {
|
|
2751
|
+
if (ev.relatedTarget && !$('ghAdopt').contains(ev.relatedTarget)) closeRepoList();
|
|
2752
|
+
});
|
|
2631
2753
|
$('ghList').addEventListener('click', function (ev) {
|
|
2632
2754
|
var row = ev.target.closest('.ghrow'); if (!row) return;
|
|
2633
2755
|
var full = row.getAttribute('data-full'); var slash = full.indexOf('/');
|
|
@@ -2944,6 +3066,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
2944
3066
|
var live = { instStatus: null, manifest: null, manifestMsg: '', manifestCode: '', appSlug: null };
|
|
2945
3067
|
var planPromises = {}; /* mode|domain → Promise<steps[]> */
|
|
2946
3068
|
var lastStepsHtml = '';
|
|
3069
|
+
var lastProgHtml = ''; /* same change-only contract, for the progress strip */
|
|
2947
3070
|
var FALLBACK_HUMAN_KEYS = ['github-repo', 'oauth-app', 'device-flow', 'app-install', 'first-sign-in', 'enter-hall'];
|
|
2948
3071
|
|
|
2949
3072
|
function fetchPlan() {
|
|
@@ -3279,22 +3402,39 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3279
3402
|
revive: { names: ['Revive queued', 'Standing it up', 'Back live'], cls: '' },
|
|
3280
3403
|
teardown: { names: ['Teardown queued', 'Winding down', 'Torn down'], cls: 'flowDown' }
|
|
3281
3404
|
};
|
|
3405
|
+
/* No live-region attributes here: this strip is destroyed and rebuilt on
|
|
3406
|
+
every 5s tick, and a live region that is itself replaced never announces.
|
|
3407
|
+
#liveProgress is the stable region; this is only its contents. (The
|
|
3408
|
+
mine-card twin stripMarkup() still carries its own — left for a later pass.) */
|
|
3282
3409
|
function progressMarkup(stageNow, stagesDone, note, cls, flow) {
|
|
3283
3410
|
var f = PROG_FLOWS[flow || 'standup'];
|
|
3284
|
-
return '<div class="prog ' + (f.cls ? f.cls + ' ' : '') + (cls || '') + '"
|
|
3411
|
+
return '<div class="prog ' + (f.cls ? f.cls + ' ' : '') + (cls || '') + '">' +
|
|
3285
3412
|
'<ul class="stages">' + f.names.map(function (n, i) {
|
|
3286
3413
|
var c = stagesDone > i ? 'done' : (stageNow === i ? 'now' : '');
|
|
3287
|
-
|
|
3414
|
+
/* the dot carries the state for the eye only — a screen reader needs
|
|
3415
|
+
the word, and the live stage marked, or it hears three bare names */
|
|
3416
|
+
var word = c === 'done' ? 'done' : (c === 'now' ? 'current' : 'upcoming');
|
|
3417
|
+
return '<li class="' + c + '"' + (c === 'now' ? ' aria-current="step"' : '') + '>' +
|
|
3418
|
+
'<span class="sdot"></span><span class="slbl">' + n + '</span>' +
|
|
3419
|
+
'<span class="vh">, ' + word + '</span></li>';
|
|
3288
3420
|
}).join('') + '</ul>' +
|
|
3289
3421
|
'<p class="pnote">' + note + '</p></div>';
|
|
3290
3422
|
}
|
|
3423
|
+
/* Change-only write into the stable live region: an identical tick performs
|
|
3424
|
+
no DOM write at all, so the region announces real movement only — the same
|
|
3425
|
+
contract renderSteps() uses with lastStepsHtml. */
|
|
3426
|
+
function setProgress(html) {
|
|
3427
|
+
var el = $('liveProgress');
|
|
3428
|
+
if (!el || html === lastProgHtml) return;
|
|
3429
|
+
lastProgHtml = html;
|
|
3430
|
+
el.innerHTML = html;
|
|
3431
|
+
}
|
|
3291
3432
|
/* Renders the current instance state; returns true when the state is terminal
|
|
3292
3433
|
(no further polling wanted). The strip is chosen by the OPEN INTENT first,
|
|
3293
3434
|
then status (F22): a torn-down instance with a queued revive must never be
|
|
3294
3435
|
told to "start another project". */
|
|
3295
3436
|
function renderProgress(inst) {
|
|
3296
|
-
|
|
3297
|
-
if (!el) return true;
|
|
3437
|
+
if (!$('liveProgress')) return true;
|
|
3298
3438
|
var s = inst && inst.status;
|
|
3299
3439
|
var revive = !!(inst && inst.torn_down_at);
|
|
3300
3440
|
var oi = inst && inst.open_intent;
|
|
@@ -3303,23 +3443,23 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3303
3443
|
would make the status branch dead and diverge from the mine card) */
|
|
3304
3444
|
if (oi && s !== 'provisioning' && s !== 'tearing_down' && s !== 'requested') {
|
|
3305
3445
|
if (oi.action === 'provision') {
|
|
3306
|
-
|
|
3446
|
+
setProgress(progressMarkup(0, 0,
|
|
3307
3447
|
(s === 'torn_down' ? 'A revive is queued' : 'A standup is queued') + ' — this panel updates when it starts.',
|
|
3308
|
-
'', s === 'torn_down' ? 'revive' : 'standup');
|
|
3448
|
+
'', s === 'torn_down' ? 'revive' : 'standup'));
|
|
3309
3449
|
return false;
|
|
3310
3450
|
}
|
|
3311
3451
|
if (oi.action === 'teardown') {
|
|
3312
|
-
|
|
3452
|
+
setProgress(progressMarkup(0, 0, 'A teardown is queued — this panel updates when it starts.', '', 'teardown'));
|
|
3313
3453
|
return false;
|
|
3314
3454
|
}
|
|
3315
3455
|
}
|
|
3316
3456
|
if (s === 'requested') {
|
|
3317
|
-
|
|
3457
|
+
setProgress(progressMarkup(0, 0, 'Filed — waiting for the platform to pick it up. This updates the moment it starts.', '', revive ? 'revive' : 'standup'));
|
|
3318
3458
|
} else if (s === 'provisioning') {
|
|
3319
|
-
|
|
3459
|
+
setProgress(progressMarkup(1, 1,
|
|
3320
3460
|
revive ? 'Bringing it back on the newest platform version — nothing needed from you.'
|
|
3321
3461
|
: 'Raising the server, database, and address — nothing needed from you.',
|
|
3322
|
-
'', revive ? 'revive' : 'standup');
|
|
3462
|
+
'', revive ? 'revive' : 'standup'));
|
|
3323
3463
|
} else if (s === 'active') {
|
|
3324
3464
|
/* Live lights on a VERIFIED answer, not on script completion (direction
|
|
3325
3465
|
§7): the strip keys on the same last_verified_* overlay the manage
|
|
@@ -3333,39 +3473,39 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3333
3473
|
/* domainless instances are never probed — there is no address to
|
|
3334
3474
|
answer at, so a verified answer can never arrive; say what IS true
|
|
3335
3475
|
instead of promising the hall (sign-in stays locked without one) */
|
|
3336
|
-
|
|
3337
|
-
'The build finished — your project is standing, but it has no address yet, so sign-in can’t be wired up. Attach a domain any time from <a href="?view=mine">your projects page</a>; the steps below that need one unlock then.', '', flow);
|
|
3476
|
+
setProgress(progressMarkup(-1, 2,
|
|
3477
|
+
'The build finished — your project is standing, but it has no address yet, so sign-in can’t be wired up. Attach a domain any time from <a href="?view=mine">your projects page</a>; the steps below that need one unlock then.', '', flow));
|
|
3338
3478
|
return true;
|
|
3339
3479
|
}
|
|
3340
3480
|
if (inst.last_verified_up === false) {
|
|
3341
|
-
|
|
3342
|
-
'The build finished, but ' + link + ' isn’t answering right now — this panel keeps checking.', '', flow);
|
|
3481
|
+
setProgress(progressMarkup(2, 2,
|
|
3482
|
+
'The build finished, but ' + link + ' isn’t answering right now — this panel keeps checking.', '', flow));
|
|
3343
3483
|
return false;
|
|
3344
3484
|
}
|
|
3345
3485
|
if (inst.last_verified_up) {
|
|
3346
|
-
|
|
3347
|
-
'Your project is answering at ' + link + '. Finish the steps below and the hall is yours.', 'isLive', flow);
|
|
3486
|
+
setProgress(progressMarkup(-1, 3,
|
|
3487
|
+
'Your project is answering at ' + link + '. Finish the steps below and the hall is yours.', 'isLive', flow));
|
|
3348
3488
|
return true;
|
|
3349
3489
|
}
|
|
3350
3490
|
/* null: standing, not checked yet — keep watching until the first probe */
|
|
3351
|
-
|
|
3352
|
-
'The build finished — checking that ' + link + ' answers…', '', flow);
|
|
3491
|
+
setProgress(progressMarkup(2, 2,
|
|
3492
|
+
'The build finished — checking that ' + link + ' answers…', '', flow));
|
|
3353
3493
|
return false;
|
|
3354
3494
|
} else if (s === 'error') {
|
|
3355
3495
|
/* the honest snag (direction §5 / F19): no raw stderr as the headline, no
|
|
3356
3496
|
false "keeps retrying" — retries stop, and the retry door is the mine view */
|
|
3357
|
-
|
|
3358
|
-
'The standup stopped partway — it won’t continue on its own. Retry it (and see the technical details) from <a href="?view=mine">your projects page</a>.', 'isErr');
|
|
3497
|
+
setProgress(progressMarkup(1, 1,
|
|
3498
|
+
'The standup stopped partway — it won’t continue on its own. Retry it (and see the technical details) from <a href="?view=mine">your projects page</a>.', 'isErr'));
|
|
3359
3499
|
} else if (s === 'tearing_down') {
|
|
3360
3500
|
/* a deliberate end is calm, never styled as a failure (direction §5) —
|
|
3361
3501
|
and it speaks the teardown vocabulary, not the standup's (F22) */
|
|
3362
|
-
|
|
3363
|
-
(inst.hosting_shape === 'dedicated' ? 'its database goes with the machine.' : 'the database is kept.'), '', 'teardown');
|
|
3502
|
+
setProgress(progressMarkup(1, 1, 'Taking the server offline. The repository stays yours; ' +
|
|
3503
|
+
(inst.hosting_shape === 'dedicated' ? 'its database goes with the machine.' : 'the database is kept.'), '', 'teardown'));
|
|
3364
3504
|
} else if (s === 'torn_down') {
|
|
3365
|
-
|
|
3505
|
+
setProgress(progressMarkup(-1, 3, 'This instance was torn down. Bring it back any time from <a href="?view=mine">your projects page</a>, or start another below.', '', 'teardown'));
|
|
3366
3506
|
return true;
|
|
3367
3507
|
} else {
|
|
3368
|
-
|
|
3508
|
+
setProgress(progressMarkup(0, 0, 'Filed. Watching for the standup to start…'));
|
|
3369
3509
|
}
|
|
3370
3510
|
return false;
|
|
3371
3511
|
}
|
|
@@ -3377,8 +3517,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3377
3517
|
var myGen = watchGen;
|
|
3378
3518
|
var ticks = 0;
|
|
3379
3519
|
var watchFlow = 'standup'; /* the vocabulary the strip is speaking (F22) */
|
|
3380
|
-
|
|
3381
|
-
el.innerHTML = progressMarkup(0, 0, 'Filed. Watching for the standup to start…');
|
|
3520
|
+
setProgress(progressMarkup(0, 0, 'Filed. Watching for the standup to start…'));
|
|
3382
3521
|
function tick() {
|
|
3383
3522
|
if (myGen !== watchGen) return;
|
|
3384
3523
|
if (!wizPollActive()) { watchPending = true; stopPolling(); return; }
|
|
@@ -3391,8 +3530,8 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3391
3530
|
/* a SNAGGED standup keeps its honest error — "still building" would be
|
|
3392
3531
|
a lie exactly when the founder most needs the truth */
|
|
3393
3532
|
if (live.instStatus !== 'error') {
|
|
3394
|
-
|
|
3395
|
-
'Still working — this tab will stop watching now. Follow it any time on <a href="?view=mine">your projects page</a>.', '', watchFlow);
|
|
3533
|
+
setProgress(progressMarkup(1, 1,
|
|
3534
|
+
'Still working — this tab will stop watching now. Follow it any time on <a href="?view=mine">your projects page</a>.', '', watchFlow));
|
|
3396
3535
|
}
|
|
3397
3536
|
stopPolling();
|
|
3398
3537
|
return;
|
|
@@ -3424,7 +3563,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3424
3563
|
if (myGen !== watchGen) return;
|
|
3425
3564
|
var m = String(err && err.message || '');
|
|
3426
3565
|
if (m.indexOf('gone:') === 0) {
|
|
3427
|
-
|
|
3566
|
+
setProgress(progressMarkup(1, 1, m.slice(5), 'isErr', watchFlow));
|
|
3428
3567
|
stopPolling();
|
|
3429
3568
|
return;
|
|
3430
3569
|
}
|
|
@@ -3449,6 +3588,7 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
3449
3588
|
manifestStartedAt = 0; manifestStale = false;
|
|
3450
3589
|
live = { instStatus: null, manifest: null, manifestMsg: '', manifestCode: '', appSlug: null };
|
|
3451
3590
|
lastStepsHtml = '';
|
|
3591
|
+
lastProgHtml = '';
|
|
3452
3592
|
try { sessionStorage.removeItem(CREATED_KEY); sessionStorage.removeItem(DRAFT_KEY); } catch (e) {}
|
|
3453
3593
|
state = { step: 1, name: '', projType: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false };
|
|
3454
3594
|
$('newName').value = ''; $('f-owner').value = ''; $('f-repo').value = ''; $('f-slug').value = ''; $('f-domain').value = '';
|
package/src/module-api.js
CHANGED
|
@@ -1108,8 +1108,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
1108
1108
|
// landed since 1.19.337 with no explicit bump. run 32796733697. (task 1002620)
|
|
1109
1109
|
// 1.19.339 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1110
1110
|
// landed since 1.19.338 with no explicit bump. run 32797325535. (task 1002620)
|
|
1111
|
+
// 1.19.340 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1112
|
+
// landed since 1.19.339 with no explicit bump. run 32798342738. (task 1002620)
|
|
1111
1113
|
// ---------------------------------------------------------------------------
|
|
1112
|
-
const CORE_VERSION = '1.19.
|
|
1114
|
+
const CORE_VERSION = '1.19.340'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
1113
1115
|
|
|
1114
1116
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
1115
1117
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|