@bongos/core 1.19.83 → 1.19.85
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 +52 -22
- package/docs/design/mocks/component-sheet.html +428 -0
- package/docs/design/mocks/projects-page-mock.html +167 -0
- package/docs/design/projects-surfaces-redesign-direction.md +3 -2
- package/modules/lifecycle/github-push.js +34 -15
- package/modules/provisioning/migrations/provisioning_006_liveness.sql +30 -0
- package/modules/provisioning/module.json +2 -1
- package/modules/provisioning/onboard-plan.js +20 -4
- package/modules/provisioning/pollers/liveness-sweep.js +266 -0
- package/modules/provisioning/provisioning.js +43 -0
- package/modules/provisioning/routes/provisioning.js +6 -2
- package/modules/provisioning/tests/liveness.mjs +420 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +311 -0
- package/public-landing/create.html +253 -125
- package/public-landing/gate/index.html +3 -0
- package/public-landing/gate/space.css +11 -1
- package/public-landing/projects.html +298 -213
- package/src/module-api.js +5 -1
- package/tests/publish_branch_route.mjs +30 -8
- package/tests/publish_reconciler.mjs +14 -7
|
@@ -11,24 +11,31 @@
|
|
|
11
11
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
12
12
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
13
13
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Quicksand:wght@400;500;600;700&display=swap">
|
|
14
|
+
<!-- The R07 redesign (task 1002696): shared token + component layer — the space
|
|
15
|
+
sky both modes ride on, light/dark governing the cards. The server re-injects
|
|
16
|
+
the branding :root AFTER stylesheets, so the pack stays authoritative.
|
|
17
|
+
Design contract: docs/design/projects-surfaces-redesign-direction.md -->
|
|
18
|
+
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
19
|
+
<script>
|
|
20
|
+
/* apply the saved color mode BEFORE first paint so there's no flash;
|
|
21
|
+
no saved choice = follow the OS (prefers-color-scheme) */
|
|
22
|
+
try{var _m=localStorage.getItem('cb-color-mode');if(_m==='light'||_m==='dark')document.documentElement.setAttribute('data-mode',_m);}catch(e){}
|
|
23
|
+
</script>
|
|
14
24
|
<style>
|
|
15
|
-
/* ──
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
/* ── /create page-specific layout. Tokens, the reset, the sky, the header voice
|
|
26
|
+
and the shared card/chip/button vocabulary all come from cosmos.css above;
|
|
27
|
+
what stays here is this page's own structure (the wizard) plus token-built
|
|
28
|
+
variants of it.
|
|
29
|
+
DUAL MODE (task 1002696, owner decision 2026-08-12): light = the sunrise
|
|
30
|
+
white/orange page, dark = the gate's deep-space sky with dark-navy cards. The
|
|
31
|
+
card INTERIOR flips too — so no literal color may live in this block; every
|
|
32
|
+
surface here is a cosmos token or a color-mix over one. (A local :root token
|
|
33
|
+
block would land after cosmos's dark tiers and silently defeat them — that is
|
|
34
|
+
why the old one is gone.)
|
|
18
35
|
The /create page is the branded project-creation experience (goal 35, task 2081):
|
|
19
36
|
one page, no framework, the same trust boundary as the hall wizard — it only
|
|
20
37
|
ENQUEUES a provisioning request and then hands the human the steps only they
|
|
21
38
|
can do. ── */
|
|
22
|
-
:root{
|
|
23
|
-
--bg:#fff7ed; --bg-deep:#ffedd5;
|
|
24
|
-
--ink:#2a1f17; --ink-soft:#6b5d52; --ink-faint:#9c8d80;
|
|
25
|
-
--accent:#f97316; --accent-deep:#ea580c; --sea:#38bdf8; --terracotta:#fb7185;
|
|
26
|
-
--card:#fffdfa; --line:#f3e1c9; --ok:#16a34a; --err:#dc2626;
|
|
27
|
-
--font-display:"Baloo 2","Quicksand",system-ui,"Arial Rounded MT Bold",sans-serif;
|
|
28
|
-
--font-body:"Quicksand",system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
29
|
-
--font-mono:"Fira Code",ui-monospace,SFMono-Regular,Menlo,monospace;
|
|
30
|
-
}
|
|
31
|
-
*{box-sizing:border-box;margin:0;padding:0;}
|
|
32
39
|
/* The `hidden` attribute must always win. Several blocks below set an explicit
|
|
33
40
|
`display` via a class (.field→block, .row2→grid); an author rule like that
|
|
34
41
|
silently defeats the user-agent [hidden] rule, so `el.hidden = true` fails to
|
|
@@ -36,52 +43,18 @@
|
|
|
36
43
|
adopt-picker / fresh-name / manual sub-panels — without this they ALL render at
|
|
37
44
|
once. Make hidden authoritative for the whole page. */
|
|
38
45
|
[hidden]{display:none !important;}
|
|
39
|
-
|
|
40
|
-
body{
|
|
41
|
-
font-family:var(--font-body); color:var(--ink);
|
|
42
|
-
background:var(--bg);
|
|
43
|
-
min-height:100vh; -webkit-font-smoothing:antialiased; overflow-x:hidden;
|
|
44
|
-
}
|
|
45
|
-
/* ── the sky — a fixed layer carrying the dawn gradient + clouds. The gradient
|
|
46
|
-
lives HERE (not background-attachment:fixed on body): fixed-attachment
|
|
47
|
-
backgrounds black out on scrolled composited pages in Chromium, and /create
|
|
48
|
-
is the first landing page tall enough to scroll. ── */
|
|
49
|
-
.sky{
|
|
50
|
-
position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
|
|
51
|
-
/* dawn at altitude: a whisper of sky up top settling into the sunrise warmth */
|
|
52
|
-
background:linear-gradient(180deg,#e9f5fd 0%,#fde8c8 34%,#fff4e6 62%,#fffdfa 100%);
|
|
53
|
-
}
|
|
54
|
-
.cloud{position:absolute;border-radius:50%;background:rgba(255,255,255,0.55);filter:blur(38px);}
|
|
55
|
-
.cloud.a{width:380px;height:380px;top:-120px;left:-80px;animation:drift 26s ease-in-out infinite alternate;}
|
|
56
|
-
.cloud.b{width:300px;height:300px;top:4%;right:-60px;background:rgba(255,224,178,0.5);animation:drift 32s ease-in-out infinite alternate-reverse;}
|
|
57
|
-
.cloud.c{width:460px;height:460px;bottom:-200px;left:24%;background:rgba(255,255,255,0.4);animation:drift 40s ease-in-out infinite alternate;}
|
|
58
|
-
.cloud.d{width:230px;height:230px;top:38%;left:2%;background:rgba(56,189,248,0.14);animation:drift 30s ease-in-out infinite alternate-reverse;}
|
|
59
|
-
.cloud.e{width:200px;height:200px;top:16%;left:56%;background:rgba(56,189,248,0.10);animation:drift 36s ease-in-out infinite alternate;}
|
|
60
|
-
@keyframes drift{from{transform:translateX(-18px)}to{transform:translateX(26px)}}
|
|
61
|
-
|
|
62
|
-
/* ── header ── */
|
|
63
|
-
.top{
|
|
64
|
-
position:relative;z-index:2;display:flex;align-items:center;justify-content:space-between;
|
|
65
|
-
padding:20px clamp(18px,4vw,40px);
|
|
66
|
-
}
|
|
67
|
-
.top .mark{
|
|
68
|
-
font-family:var(--font-display);font-weight:700;font-size:22px;letter-spacing:-0.5px;
|
|
69
|
-
text-transform:lowercase;color:var(--ink);text-decoration:none;
|
|
70
|
-
}
|
|
71
|
-
.top .mark span{color:var(--accent);}
|
|
72
|
-
.top-right{display:flex;align-items:center;gap:clamp(10px,2vw,18px);}
|
|
46
|
+
|
|
73
47
|
/* exit the wizard — Esc, or this button (task 2098). Nothing is created until the
|
|
74
48
|
final step, so leaving simply drops the in-progress project. */
|
|
75
49
|
.exit-wizard{
|
|
76
50
|
display:inline-flex;align-items:center;gap:7px;cursor:pointer;
|
|
77
|
-
background:
|
|
78
|
-
padding:7px 14px;font-family:var(--font-body);font-weight:600;font-size:13px;color:var(--ink
|
|
51
|
+
background:var(--glass-bg);border:1.5px solid var(--glass-border);border-radius:999px;
|
|
52
|
+
padding:7px 14px;font-family:var(--font-body);font-weight:600;font-size:13px;color:var(--glass-ink);
|
|
53
|
+
text-shadow:var(--chrome-glow);backdrop-filter:blur(4px);
|
|
79
54
|
transition:background .14s ease,border-color .14s ease,color .14s ease;
|
|
80
55
|
}
|
|
81
|
-
.exit-wizard:hover{
|
|
82
|
-
.exit-wizard kbd{font-family:var(--font-mono);font-size:10px;line-height:1;background:
|
|
83
|
-
.who{display:flex;align-items:center;gap:10px;font-size:14px;font-weight:600;color:var(--ink-soft);min-height:34px;}
|
|
84
|
-
.who img{width:30px;height:30px;border-radius:50%;border:2px solid #fff;box-shadow:0 2px 8px rgba(42,31,23,0.12);}
|
|
56
|
+
.exit-wizard:hover{border-color:var(--chrome-accent);color:var(--chrome-accent);}
|
|
57
|
+
.exit-wizard kbd{font-family:var(--font-mono);font-size:10px;line-height:1;background:color-mix(in srgb,var(--chrome-ink) 12%,transparent);border-radius:4px;padding:2px 5px;color:var(--chrome-ink-dim);}
|
|
85
58
|
|
|
86
59
|
/* ── layout ── */
|
|
87
60
|
.wrap{
|
|
@@ -101,20 +74,20 @@ body{
|
|
|
101
74
|
}
|
|
102
75
|
.guide .mascot img{display:block;width:100%;height:auto;}
|
|
103
76
|
.bubble{
|
|
104
|
-
position:relative;background
|
|
77
|
+
position:relative;background:var(--bg-card);border-radius:18px;padding:14px 18px;max-width:280px;
|
|
105
78
|
font-size:15px;font-weight:600;color:var(--ink-soft);text-align:center;line-height:1.45;
|
|
106
|
-
box-shadow:0 10px 30px -12px rgba(
|
|
79
|
+
box-shadow:0 10px 30px -12px rgba(5,6,15,0.35);
|
|
107
80
|
}
|
|
108
81
|
.bubble::after{
|
|
109
82
|
content:"";position:absolute;top:-8px;left:50%;transform:translateX(-50%) rotate(45deg);
|
|
110
|
-
width:16px;height:16px;background
|
|
83
|
+
width:16px;height:16px;background:var(--bg-card);border-radius:3px;
|
|
111
84
|
}
|
|
112
85
|
.guide .bubble{order:-1;margin-bottom:16px;}
|
|
113
86
|
|
|
114
87
|
/* the wizard card — a cloud you build on */
|
|
115
88
|
.card{
|
|
116
89
|
flex:1;min-width:0;background:var(--card);border-radius:26px;
|
|
117
|
-
box-shadow:
|
|
90
|
+
box-shadow:var(--shadow-card);
|
|
118
91
|
padding:clamp(22px,3.6vw,40px);
|
|
119
92
|
animation:rise .7s ease-out .08s both;
|
|
120
93
|
}
|
|
@@ -130,11 +103,11 @@ body{
|
|
|
130
103
|
.beats li.done:not(:first-child)::before{background:var(--accent);}
|
|
131
104
|
.beats .dot{
|
|
132
105
|
position:relative;z-index:1;width:28px;height:28px;border-radius:50%;
|
|
133
|
-
border:3px solid var(--line);background
|
|
106
|
+
border:3px solid var(--line);background:var(--bg-card);display:flex;align-items:center;justify-content:center;
|
|
134
107
|
font-family:var(--font-display);font-weight:700;font-size:13px;color:var(--ink-faint);
|
|
135
108
|
transition:all .25s ease;
|
|
136
109
|
}
|
|
137
|
-
.beats li.now .dot{border-color:var(--accent);color:var(--accent-deep);box-shadow:0 0 0 6px
|
|
110
|
+
.beats li.now .dot{border-color:var(--accent);color:var(--accent-deep);box-shadow:0 0 0 6px color-mix(in srgb,var(--accent) 14%,transparent);}
|
|
138
111
|
.beats li.done .dot{border-color:var(--accent);background:var(--accent);color:#fff;}
|
|
139
112
|
.beats .lbl{font-size:12px;font-weight:700;color:var(--ink-faint);letter-spacing:0.2px;}
|
|
140
113
|
.beats li.now .lbl{color:var(--accent-deep);}
|
|
@@ -145,20 +118,21 @@ body{
|
|
|
145
118
|
.panel.on{display:block;animation:fadeStep .3s ease both;}
|
|
146
119
|
@keyframes fadeStep{from{opacity:0;transform:translateX(10px)}to{opacity:1;transform:none}}
|
|
147
120
|
h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32px);letter-spacing:-0.5px;line-height:1.15;margin-bottom:8px;}
|
|
121
|
+
/* show() moves focus to each panel's h1 for keyboard/SR context — keep the
|
|
122
|
+
focus, lose the ring (a black rectangle around a heading; owner walk) */
|
|
123
|
+
h1[tabindex="-1"]:focus{outline:none;}
|
|
148
124
|
.lede{font-size:15.5px;color:var(--ink-soft);font-weight:500;line-height:1.55;margin-bottom:24px;max-width:56ch;}
|
|
149
|
-
.lede a{color:var(--accent-deep);font-weight:700;text-decoration:none;}
|
|
150
|
-
.lede a:hover{text-decoration:underline;}
|
|
151
125
|
|
|
152
126
|
.field{display:block;margin-bottom:18px;}
|
|
153
127
|
.field>span{display:block;font-size:13.5px;font-weight:700;color:var(--ink-soft);margin-bottom:7px;letter-spacing:0.2px;}
|
|
154
128
|
.field small{font-weight:500;color:var(--ink-faint);}
|
|
155
129
|
input[type=text]{
|
|
156
130
|
width:100%;font-family:var(--font-body);font-size:17px;font-weight:600;color:var(--ink);
|
|
157
|
-
background
|
|
131
|
+
background:var(--bg-card);border:2px solid var(--line);border-radius:14px;padding:13px 16px;outline:none;
|
|
158
132
|
transition:border-color .15s ease, box-shadow .15s ease;
|
|
159
133
|
}
|
|
160
|
-
input[type=text]::placeholder{color
|
|
161
|
-
input[type=text]:focus{border-color:var(--accent);box-shadow:0 0 0 4px
|
|
134
|
+
input[type=text]::placeholder{color:color-mix(in srgb,var(--ink-faint) 70%,var(--bg-card));font-weight:500;}
|
|
135
|
+
input[type=text]:focus{border-color:var(--accent);box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 13%,transparent);}
|
|
162
136
|
input[type=text].bad{border-color:var(--err);}
|
|
163
137
|
.hint{font-size:13px;font-weight:600;color:var(--ink-faint);margin-top:7px;min-height:18px;}
|
|
164
138
|
.hint.ok{color:var(--ok);}
|
|
@@ -170,17 +144,18 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
170
144
|
/* choice cards (greenfield / adopt) */
|
|
171
145
|
.choices{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:18px;}
|
|
172
146
|
.choice{
|
|
173
|
-
text-align:left;cursor:pointer;background
|
|
147
|
+
text-align:left;cursor:pointer;background:var(--bg-card);border:2px solid var(--line);border-radius:18px;
|
|
174
148
|
padding:16px;font-family:var(--font-body);transition:all .15s ease;
|
|
175
149
|
}
|
|
176
|
-
.choice:hover{border-color
|
|
177
|
-
.choice[aria-pressed=true]{border-color:var(--accent);background
|
|
150
|
+
.choice:hover{border-color:var(--rule);transform:translateY(-1px);}
|
|
151
|
+
.choice[aria-pressed=true]{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 5%,var(--bg-card));box-shadow:0 8px 24px -12px color-mix(in srgb,var(--accent) 35%,transparent);}
|
|
178
152
|
.choice svg{display:block;margin-bottom:9px;}
|
|
179
153
|
.choice b{display:block;font-size:15.5px;font-weight:700;margin-bottom:3px;}
|
|
180
154
|
.choice p{font-size:13px;font-weight:500;color:var(--ink-soft);line-height:1.45;}
|
|
181
155
|
.reassure{
|
|
182
|
-
display:none;background
|
|
183
|
-
|
|
156
|
+
display:none;background:color-mix(in srgb,var(--sea) 8%,var(--bg-card));
|
|
157
|
+
border:1.5px solid color-mix(in srgb,var(--sea) 25%,var(--bg-card));border-radius:14px;
|
|
158
|
+
padding:12px 15px;font-size:13.5px;font-weight:500;color:color-mix(in srgb,var(--sea) 40%,var(--ink));line-height:1.5;margin-bottom:18px;
|
|
184
159
|
}
|
|
185
160
|
.reassure.on{display:block;}
|
|
186
161
|
|
|
@@ -210,14 +185,14 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
210
185
|
}
|
|
211
186
|
.btn:hover{transform:translateY(-1px);box-shadow:0 16px 34px -10px var(--accent);}
|
|
212
187
|
.btn:active{transform:translateY(0) scale(.98);}
|
|
213
|
-
.btn:disabled{background
|
|
188
|
+
.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;}
|
|
214
189
|
.btn.ghost{background:transparent;color:var(--ink-soft);box-shadow:none;border:2px solid var(--line);padding:12px 22px;}
|
|
215
|
-
.btn.ghost:hover{border-color
|
|
190
|
+
.btn.ghost:hover{border-color:var(--rule-soft);color:var(--ink);}
|
|
216
191
|
.btn.big{width:100%;justify-content:center;font-size:18px;padding:17px 30px;}
|
|
217
192
|
.gh{width:20px;height:20px;fill:currentColor;}
|
|
218
193
|
|
|
219
194
|
.alert{
|
|
220
|
-
display:none;background
|
|
195
|
+
display:none;background:var(--err-soft);border:1.5px solid color-mix(in srgb,var(--err) 25%,transparent);border-radius:14px;
|
|
221
196
|
padding:12px 15px;font-size:14px;font-weight:600;color:var(--err);line-height:1.5;margin-top:16px;
|
|
222
197
|
}
|
|
223
198
|
.alert.on{display:block;}
|
|
@@ -241,7 +216,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
241
216
|
@keyframes pop{from{transform:scale(.4);opacity:0}to{transform:scale(1);opacity:1}}
|
|
242
217
|
.steps{list-style:none;counter-reset:hs;margin-top:20px;}
|
|
243
218
|
.steps>li{
|
|
244
|
-
position:relative;background
|
|
219
|
+
position:relative;background:var(--bg-card);border:2px solid var(--line);border-radius:18px;
|
|
245
220
|
padding:16px 18px 16px 58px;margin-bottom:12px;counter-increment:hs;
|
|
246
221
|
}
|
|
247
222
|
.steps>li::before{
|
|
@@ -253,7 +228,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
253
228
|
.steps p{font-size:13.5px;font-weight:500;color:var(--ink-soft);line-height:1.55;margin-top:4px;}
|
|
254
229
|
/* live checklist states (task 1002694): a step the platform has verified done, and a
|
|
255
230
|
step that can't be performed yet (its prerequisite — usually the domain — is missing) */
|
|
256
|
-
.steps>li.sdone{border-color
|
|
231
|
+
.steps>li.sdone{border-color:color-mix(in srgb,var(--ok) 30%,var(--bg-card));background:linear-gradient(180deg,color-mix(in srgb,var(--ok) 8%,var(--bg-card)) 0%,var(--bg-card) 100%);}
|
|
257
232
|
.steps>li.sdone::before{content:"✓";background:var(--ok);color:#fff;}
|
|
258
233
|
.steps>li.slocked{opacity:.6;}
|
|
259
234
|
.steps details{margin-top:10px;}
|
|
@@ -266,7 +241,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
266
241
|
.kv .kk{font-weight:700;color:var(--ink-faint);flex:0 0 auto;}
|
|
267
242
|
.kv code{font-family:var(--font-mono);font-size:12px;color:var(--accent-deep);word-break:break-all;flex:1;}
|
|
268
243
|
.copy{
|
|
269
|
-
flex:0 0 auto;cursor:pointer;border:none;border-radius:8px;background
|
|
244
|
+
flex:0 0 auto;cursor:pointer;border:none;border-radius:8px;background:var(--bg-card);border:1.5px solid var(--line);
|
|
270
245
|
font-family:var(--font-body);font-size:11.5px;font-weight:700;color:var(--ink-soft);padding:5px 10px;
|
|
271
246
|
transition:all .12s ease;
|
|
272
247
|
}
|
|
@@ -277,19 +252,19 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
277
252
|
|
|
278
253
|
/* ── live build progress (task 2083) ── */
|
|
279
254
|
.prog{
|
|
280
|
-
background:linear-gradient(180deg
|
|
281
|
-
border:2px solid
|
|
255
|
+
background:linear-gradient(180deg,color-mix(in srgb,var(--sea) 8%,var(--bg-card)) 0%,var(--bg-card) 100%);
|
|
256
|
+
border:2px solid var(--sea-soft);border-radius:18px;padding:18px 20px 16px;margin-top:14px;
|
|
282
257
|
}
|
|
283
258
|
.prog .stages{display:flex;list-style:none;margin-bottom:4px;}
|
|
284
259
|
.prog .stages li{flex:1;display:flex;flex-direction:column;align-items:center;gap:7px;position:relative;}
|
|
285
260
|
.prog .stages li:not(:first-child)::before{
|
|
286
261
|
content:"";position:absolute;top:11px;right:50%;width:100%;height:3px;border-radius:2px;
|
|
287
|
-
background
|
|
262
|
+
background:var(--sea-soft);z-index:0;margin-right:12px;
|
|
288
263
|
}
|
|
289
264
|
.prog .stages li.done:not(:first-child)::before{background:var(--sea);}
|
|
290
265
|
.prog .sdot{
|
|
291
266
|
position:relative;z-index:1;width:24px;height:24px;border-radius:50%;
|
|
292
|
-
border:3px solid
|
|
267
|
+
border:3px solid var(--sea-soft);background:var(--bg-card);display:flex;align-items:center;justify-content:center;
|
|
293
268
|
}
|
|
294
269
|
.prog .stages li.done .sdot{border-color:var(--sea);background:var(--sea);}
|
|
295
270
|
.prog .stages li.done .sdot::after{content:"";width:8px;height:5px;border-left:2.5px solid #fff;border-bottom:2.5px solid #fff;transform:rotate(-45deg) translate(1px,-1px);}
|
|
@@ -299,18 +274,18 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
299
274
|
animation:beatPulse 1.2s ease-in-out infinite;
|
|
300
275
|
}
|
|
301
276
|
@keyframes beatPulse{0%,100%{transform:scale(.7);opacity:.6}50%{transform:scale(1.15);opacity:1}}
|
|
302
|
-
.prog .slbl{font-size:12px;font-weight:700;color
|
|
303
|
-
.prog .stages li.now .slbl,.prog .stages li.done .slbl{color
|
|
304
|
-
.prog .pnote{text-align:center;font-size:13px;font-weight:600;color
|
|
277
|
+
.prog .slbl{font-size:12px;font-weight:700;color:color-mix(in srgb,var(--sea) 55%,var(--ink-faint));}
|
|
278
|
+
.prog .stages li.now .slbl,.prog .stages li.done .slbl{color:color-mix(in srgb,var(--sea) 40%,var(--ink));}
|
|
279
|
+
.prog .pnote{text-align:center;font-size:13px;font-weight:600;color:color-mix(in srgb,var(--sea) 45%,var(--ink-soft));min-height:18px;}
|
|
305
280
|
.prog .pnote a{color:var(--accent-deep);font-weight:700;text-decoration:none;}
|
|
306
281
|
.prog .pnote a:hover{text-decoration:underline;}
|
|
307
|
-
.prog.isLive{background:linear-gradient(180deg
|
|
282
|
+
.prog.isLive{background:linear-gradient(180deg,color-mix(in srgb,var(--ok) 8%,var(--bg-card)) 0%,var(--bg-card) 100%);border-color:color-mix(in srgb,var(--ok) 30%,var(--bg-card));}
|
|
308
283
|
.prog.isLive .stages li.done:not(:first-child)::before{background:var(--ok);}
|
|
309
284
|
.prog.isLive .stages li.done .sdot{border-color:var(--ok);background:var(--ok);}
|
|
310
|
-
.prog.isLive .slbl{color
|
|
311
|
-
.prog.isLive .stages li.done .slbl{color
|
|
312
|
-
.prog.isLive .pnote{color
|
|
313
|
-
.prog.isErr{background
|
|
285
|
+
.prog.isLive .slbl{color:var(--ok);}
|
|
286
|
+
.prog.isLive .stages li.done .slbl{color:color-mix(in srgb,var(--ok) 75%,var(--ink));}
|
|
287
|
+
.prog.isLive .pnote{color:color-mix(in srgb,var(--ok) 75%,var(--ink));font-size:14px;}
|
|
288
|
+
.prog.isErr{background:var(--err-soft);border-color:color-mix(in srgb,var(--err) 25%,var(--bg-card));}
|
|
314
289
|
.prog.isErr .pnote{color:var(--err);}
|
|
315
290
|
.again{
|
|
316
291
|
display:inline-block;margin-top:14px;font-size:13px;font-weight:700;color:var(--ink-faint);
|
|
@@ -319,7 +294,17 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
319
294
|
}
|
|
320
295
|
.again:hover{color:var(--accent-deep);}
|
|
321
296
|
|
|
322
|
-
|
|
297
|
+
/* the collapsed address step — a confirmation, not a form (task 1002704) */
|
|
298
|
+
.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;}
|
|
299
|
+
.addrConfirm.optedOut{border-color:var(--line);background:var(--bg);opacity:.75;}
|
|
300
|
+
.addrBig{font-family:var(--font-display);font-weight:700;font-size:19px;letter-spacing:-0.3px;line-height:1.4;}
|
|
301
|
+
.addrBig code{font-family:var(--font-mono);font-size:15px;background:var(--bg-deep);border-radius:8px;padding:2px 9px;color:var(--accent-deep);word-break:break-all;}
|
|
302
|
+
.addrConfirm>p{font-size:13.5px;font-weight:500;color:var(--ink-soft);line-height:1.5;margin-top:6px;}
|
|
303
|
+
.addrConfirm details{margin-top:12px;}
|
|
304
|
+
.addrConfirm summary{cursor:pointer;font-size:13px;font-weight:700;color:var(--ink-faint);}
|
|
305
|
+
.addrConfirm details .inner{margin-top:10px;}
|
|
306
|
+
.addrOpt{display:flex;align-items:flex-start;gap:8px;margin-top:12px;font-size:12.5px;font-weight:600;color:var(--warn);line-height:1.5;}
|
|
307
|
+
.addrOpt input{margin-top:2px;}
|
|
323
308
|
|
|
324
309
|
/* ── responsive ── */
|
|
325
310
|
@media (max-width:860px){
|
|
@@ -336,25 +321,37 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
336
321
|
/* GitHub repo picker (task 1002099) */
|
|
337
322
|
.linklike{background:none;border:0;padding:0;margin-left:12px;color:var(--accent-deep);font:inherit;font-size:13px;cursor:pointer;text-decoration:underline;}
|
|
338
323
|
#ghConnect .btn{margin-top:4px;}
|
|
339
|
-
.ghlist{margin-top:8px;max-height:220px;overflow-y:auto;border:1px solid var(--line
|
|
324
|
+
.ghlist{margin-top:8px;max-height:220px;overflow-y:auto;border:1px solid var(--line);border-radius:10px;background:var(--bg-deep);}
|
|
340
325
|
.ghlist:empty{display:none;}
|
|
341
|
-
|
|
326
|
+
/* rows sit on --bg-deep, so their tints mix over THAT, not the card */
|
|
327
|
+
.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;}
|
|
342
328
|
.ghrow:last-child{border-bottom:0;}
|
|
343
|
-
.ghrow:hover{background
|
|
344
|
-
.ghrow[aria-selected=true]{background
|
|
345
|
-
.ghrow .priv{margin-left:auto;font-size:11px;font-weight:600;color:var(--ink-faint
|
|
329
|
+
.ghrow:hover{background:color-mix(in srgb,var(--accent) 6%,var(--bg-deep));}
|
|
330
|
+
.ghrow[aria-selected=true]{background:color-mix(in srgb,var(--accent) 12%,var(--bg-deep));box-shadow:inset 3px 0 0 var(--accent);font-weight:600;}
|
|
331
|
+
.ghrow .priv{margin-left:auto;font-size:11px;font-weight:600;color:var(--ink-faint);background:color-mix(in srgb,var(--ink) 8%,transparent);border-radius:5px;padding:1px 6px;}
|
|
346
332
|
</style>
|
|
347
333
|
</head>
|
|
348
334
|
<body>
|
|
349
335
|
<div class="sky" aria-hidden="true">
|
|
350
|
-
<
|
|
351
|
-
<
|
|
336
|
+
<span class="star" style="top:3%;left:14%;width:2px;height:2px;"></span>
|
|
337
|
+
<span class="star" style="top:6%;left:32%;width:1.5px;height:1.5px;animation-delay:.7s;"></span>
|
|
338
|
+
<span class="star" style="top:4%;left:52%;width:2px;height:2px;animation-delay:1.4s;"></span>
|
|
339
|
+
<span class="star" style="top:9%;left:70%;width:1.5px;height:1.5px;animation-delay:.3s;"></span>
|
|
340
|
+
<span class="star" style="top:5%;left:86%;width:2.5px;height:2.5px;animation-delay:2s;"></span>
|
|
341
|
+
<span class="star" style="top:16%;left:8%;width:1.5px;height:1.5px;animation-delay:1s;"></span>
|
|
342
|
+
<span class="star" style="top:24%;left:44%;width:2px;height:2px;animation-delay:1.8s;"></span>
|
|
343
|
+
<span class="star" style="top:32%;left:80%;width:1.5px;height:1.5px;animation-delay:.5s;"></span>
|
|
344
|
+
<span class="star" style="top:46%;left:18%;width:1.5px;height:1.5px;animation-delay:2.4s;"></span>
|
|
345
|
+
<span class="star" style="top:58%;left:62%;width:2px;height:2px;animation-delay:1.2s;"></span>
|
|
346
|
+
<span class="star" style="top:72%;left:36%;width:1.5px;height:1.5px;animation-delay:.9s;"></span>
|
|
347
|
+
<span class="star" style="top:84%;left:74%;width:1.5px;height:1.5px;animation-delay:1.6s;"></span>
|
|
352
348
|
</div>
|
|
353
349
|
|
|
354
350
|
<header class="top">
|
|
355
351
|
<a class="mark" href="/"><span data-brand-product>cloud bongos</span></a>
|
|
356
352
|
<div class="top-right">
|
|
357
353
|
<button type="button" class="exit-wizard" id="exitWizard" title="Leave the wizard — nothing is created">Exit <kbd>esc</kbd></button>
|
|
354
|
+
<button type="button" class="mode-toggle" id="modeToggle" title="Switch light / dark" aria-label="Switch light or dark mode">☾</button>
|
|
358
355
|
<div class="who" id="who" aria-live="polite"></div>
|
|
359
356
|
</div>
|
|
360
357
|
</header>
|
|
@@ -420,12 +417,12 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
420
417
|
<button type="button" class="choice" id="c-green" aria-pressed="true">
|
|
421
418
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" aria-hidden="true"><path d="M15 26V14" stroke="#f97316" stroke-width="2.6" stroke-linecap="round"/><path d="M15 15c0-5 3.5-8.5 9-8.5 0 5.5-3.5 8.5-9 8.5Z" fill="#fdba74" stroke="#f97316" stroke-width="2"/><path d="M15 18c0-3.8-2.6-6.5-7-6.5 0 4.3 2.7 6.5 7 6.5Z" fill="#fed7aa" stroke="#f97316" stroke-width="2"/></svg>
|
|
422
419
|
<b>Start fresh</b>
|
|
423
|
-
<p>A new, empty repo. We scaffold everything: config, first version, kickoff tasks.</p>
|
|
420
|
+
<p>A new, empty <strong>public</strong> repo. We scaffold everything: config, first version, kickoff tasks.</p>
|
|
424
421
|
</button>
|
|
425
422
|
<button type="button" class="choice" id="c-adopt" aria-pressed="false">
|
|
426
423
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" aria-hidden="true"><rect x="4" y="9" width="22" height="16" rx="3" fill="#fed7aa" stroke="#f97316" stroke-width="2"/><path d="M4 13h22" stroke="#f97316" stroke-width="2"/><path d="M10 9V6.5A1.5 1.5 0 0 1 11.5 5h7A1.5 1.5 0 0 1 20 6.5V9" stroke="#f97316" stroke-width="2"/><path d="M12 19h6" stroke="#ea580c" stroke-width="2.4" stroke-linecap="round"/></svg>
|
|
427
424
|
<b>Adopt a repo I have</b>
|
|
428
|
-
<p>Layer Cloud Bongos onto an existing repository — history, code, and backlog intact.</p>
|
|
425
|
+
<p>Layer Cloud Bongos onto an existing repository — history, code, and backlog intact. Public or private, we never change it.</p>
|
|
429
426
|
</button>
|
|
430
427
|
</div>
|
|
431
428
|
<div class="reassure" id="adoptNote">Adopting never overwrites your files: you get a conflict pre-flight first, nothing is written until you accept, and your existing backlog (issues, TODOs) is imported as real tasks.</div>
|
|
@@ -446,11 +443,17 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
446
443
|
<input type="text" id="ghSearch" placeholder="Search your repos…" autocomplete="off">
|
|
447
444
|
<div id="ghList" class="ghlist" role="listbox" aria-label="Your repositories"></div>
|
|
448
445
|
<div class="hint" id="ghAdoptHint"> </div>
|
|
446
|
+
<!-- visibility stated on EVERY repo path (task 1002721) — the picker
|
|
447
|
+
marks private repos, and this says what adoption does about it -->
|
|
448
|
+
<div class="hint">We never change a repo’s visibility — public stays public, private stays private.</div>
|
|
449
449
|
</div>
|
|
450
450
|
<div id="ghFresh" class="field" hidden>
|
|
451
451
|
<span>New repository name</span>
|
|
452
452
|
<input type="text" id="ghNewName" placeholder="mercury" autocomplete="off" maxlength="100">
|
|
453
|
-
|
|
453
|
+
<!-- visibility is stated on EVERY repo path (task 1002721) — this baked
|
|
454
|
+
line is the floor; updateFreshPreview() replaces it with the same
|
|
455
|
+
claim plus the resolved repo URL once a name is typed -->
|
|
456
|
+
<div class="hint" id="ghFreshHint">We’ll create an empty <strong>public</strong> repo — projects here are open by design. Want private code? Create the repo yourself on GitHub and “adopt” it instead.</div>
|
|
454
457
|
</div>
|
|
455
458
|
|
|
456
459
|
<div class="row2" id="manualHome">
|
|
@@ -460,6 +463,9 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
460
463
|
<label class="field"><span>Repository name</span>
|
|
461
464
|
<input type="text" id="f-repo" placeholder="mercury" autocomplete="off" maxlength="120">
|
|
462
465
|
</label>
|
|
466
|
+
<!-- visibility stated BEFORE GitHub's own radio is the first place it
|
|
467
|
+
appears (task 1002721, owner firsthand in the QA walk) -->
|
|
468
|
+
<div class="hint" style="grid-column:1 / -1;">Creating the repo yourself on GitHub? Public or private is your choice there — both work here, and we never change it.</div>
|
|
463
469
|
</div>
|
|
464
470
|
<div class="hint" id="h-home"> </div>
|
|
465
471
|
<div class="nav">
|
|
@@ -469,19 +475,33 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
469
475
|
</form>
|
|
470
476
|
</section>
|
|
471
477
|
|
|
472
|
-
<!-- step 3 · address
|
|
478
|
+
<!-- step 3 · address — a CONFIRMATION, not a form (task 1002704: every project
|
|
479
|
+
gets its slug subdomain assigned up front; opt-out lives in the disclosure).
|
|
480
|
+
The domainless default was a proven dead-end (QA walk, task 1002702). -->
|
|
473
481
|
<section class="panel" id="panel-address" aria-labelledby="addr-h">
|
|
474
482
|
<h1 id="addr-h">Its address in the sky</h1>
|
|
475
|
-
<p class="lede">The handle names your project on the platform
|
|
483
|
+
<p class="lede">The handle names your project on the platform — and its address comes with it. Sign-in, sharing, everything just works from the moment it stands up.</p>
|
|
476
484
|
<form data-next="4">
|
|
477
485
|
<label class="field"><span>Project handle</span>
|
|
478
486
|
<input type="text" id="f-slug" placeholder="mercury" autocomplete="off" maxlength="40" spellcheck="false">
|
|
479
487
|
<div class="hint" id="h-slug"> </div>
|
|
480
488
|
</label>
|
|
481
|
-
<
|
|
482
|
-
<
|
|
483
|
-
<
|
|
484
|
-
|
|
489
|
+
<div class="addrConfirm" id="addrConfirm">
|
|
490
|
+
<div class="addrBig" id="addrAssigned"></div>
|
|
491
|
+
<p id="addrAssignedNote">Yours from the moment it stands up. You can point a domain you own at it any time.</p>
|
|
492
|
+
<details id="addrCustom">
|
|
493
|
+
<summary>Use a domain I own instead</summary>
|
|
494
|
+
<div class="inner">
|
|
495
|
+
<label class="field"><span>Domain you own</span>
|
|
496
|
+
<input type="text" id="f-domain" placeholder="mercury.example.com" autocomplete="off" maxlength="253" spellcheck="false">
|
|
497
|
+
<div class="hint" id="h-domain"> </div>
|
|
498
|
+
</label>
|
|
499
|
+
<label class="addrOpt"><input type="checkbox" id="f-noaddr">
|
|
500
|
+
<span>Start without an address — possible, but sign-in on your project can’t be set up until it has one.</span>
|
|
501
|
+
</label>
|
|
502
|
+
</div>
|
|
503
|
+
</details>
|
|
504
|
+
</div>
|
|
485
505
|
<div class="nav">
|
|
486
506
|
<button type="button" class="btn ghost" data-back="2">← Back</button>
|
|
487
507
|
<button type="submit" class="btn">Next →</button>
|
|
@@ -534,10 +554,26 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
534
554
|
document.querySelectorAll('[data-brand-product]').forEach(function (n) { n.textContent = productName.toLowerCase(); });
|
|
535
555
|
document.querySelectorAll('[data-brand-product-inline]').forEach(function (n) { n.textContent = productName; });
|
|
536
556
|
|
|
557
|
+
/* ── light/dark: the sky is constant, the mode governs the cards. No saved
|
|
558
|
+
choice = follow the OS; the toggle stores an explicit one. ── */
|
|
559
|
+
function resolvedMode() {
|
|
560
|
+
var m = document.documentElement.getAttribute('data-mode');
|
|
561
|
+
if (m === 'light' || m === 'dark') return m;
|
|
562
|
+
return (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light';
|
|
563
|
+
}
|
|
564
|
+
function paintModeToggle() { $('modeToggle').textContent = resolvedMode() === 'dark' ? '☀' : '☾'; }
|
|
565
|
+
$('modeToggle').addEventListener('click', function () {
|
|
566
|
+
var next = resolvedMode() === 'dark' ? 'light' : 'dark';
|
|
567
|
+
document.documentElement.setAttribute('data-mode', next);
|
|
568
|
+
try { localStorage.setItem('cb-color-mode', next); } catch (e) {}
|
|
569
|
+
paintModeToggle();
|
|
570
|
+
});
|
|
571
|
+
paintModeToggle();
|
|
572
|
+
|
|
537
573
|
/* ── state ── */
|
|
538
574
|
var DRAFT_KEY = 'cb-create-draft';
|
|
539
575
|
var CREATED_KEY = 'cb-created-instance';
|
|
540
|
-
var state = { step: 0, name: '', mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '' };
|
|
576
|
+
var state = { step: 0, name: '', mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false };
|
|
541
577
|
try { Object.assign(state, JSON.parse(sessionStorage.getItem(DRAFT_KEY) || '{}')); } catch (e) {}
|
|
542
578
|
var me = null;
|
|
543
579
|
|
|
@@ -591,6 +627,58 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
591
627
|
function looksLikeDomain(d) {
|
|
592
628
|
return /^(?!-)[a-z0-9-]{1,63}(?<!-)(\.(?!-)[a-z0-9-]{1,63}(?<!-))+$/i.test(String(d || ''));
|
|
593
629
|
}
|
|
630
|
+
/* ── the assigned address (task 1002704): every project gets <slug>.<apex> up
|
|
631
|
+
front, derived from the host this wizard runs on — cloudbongos.com assigns
|
|
632
|
+
slug.cloudbongos.com; a self-hosted apex assigns under its own name. On a
|
|
633
|
+
dotless/local host (localhost, an IP) there is nothing sane to assign, so
|
|
634
|
+
the step degrades to the old optional-domain form. ── */
|
|
635
|
+
function apexBase() {
|
|
636
|
+
var h = String(location.hostname || '').toLowerCase().replace(/^www\./, '');
|
|
637
|
+
if (!h || h.indexOf('.') < 0) return null;
|
|
638
|
+
if (/^(localhost|127\.|\d+\.\d+\.\d+\.\d+$|\[)/.test(h)) return null;
|
|
639
|
+
return h;
|
|
640
|
+
}
|
|
641
|
+
function assignedDomain() {
|
|
642
|
+
var base = apexBase();
|
|
643
|
+
return base && validSlug(state.slug) ? state.slug + '.' + base : null;
|
|
644
|
+
}
|
|
645
|
+
/* What the create actually files: a custom domain wins; opting out files none;
|
|
646
|
+
otherwise the assigned subdomain. Everything downstream (review, submit,
|
|
647
|
+
the done-panel steps) reads THIS, never state.domain directly. */
|
|
648
|
+
function resolvedDomain() {
|
|
649
|
+
if (state.domain) return state.domain;
|
|
650
|
+
if (state.noAddress) return '';
|
|
651
|
+
return assignedDomain() || '';
|
|
652
|
+
}
|
|
653
|
+
function renderAddrPanel() {
|
|
654
|
+
var box = $('addrConfirm');
|
|
655
|
+
if (!box) return;
|
|
656
|
+
var assigned = assignedDomain();
|
|
657
|
+
var custom = state.domain && looksLikeDomain(state.domain) ? state.domain : '';
|
|
658
|
+
box.classList.toggle('optedOut', state.noAddress && !custom);
|
|
659
|
+
var big = $('addrAssigned');
|
|
660
|
+
var note = $('addrAssignedNote');
|
|
661
|
+
if (custom) {
|
|
662
|
+
big.innerHTML = '<code>' + esc(custom) + '</code> — your own domain, wired up for you.';
|
|
663
|
+
note.textContent = 'DNS pointing happens after the standup; the card on your projects page walks you through it.';
|
|
664
|
+
} else if (state.noAddress) {
|
|
665
|
+
big.textContent = 'Starting without an address.';
|
|
666
|
+
note.textContent = 'Sign-in on your project can’t be set up until it has one — add it any time from your projects page.';
|
|
667
|
+
} else if (assigned) {
|
|
668
|
+
big.innerHTML = '<code>' + esc(assigned) + '</code> — yours from the moment it stands up.';
|
|
669
|
+
note.textContent = 'Sign-in, sharing, everything just works. You can point a domain you own at it any time.';
|
|
670
|
+
} else if (!validSlug(state.slug)) {
|
|
671
|
+
big.textContent = 'Pick a handle above and the address comes with it.';
|
|
672
|
+
note.textContent = '';
|
|
673
|
+
} else {
|
|
674
|
+
/* dotless host (local dev): nothing sane to auto-assign — the domain
|
|
675
|
+
field IS the step here, so unfold it (a folded disclosure made the
|
|
676
|
+
opt-out checkbox unreachable-by-default; owner walk, 2026-08-12) */
|
|
677
|
+
big.textContent = 'No address is assigned on this host.';
|
|
678
|
+
note.textContent = 'Add a domain you own below, or start without one.';
|
|
679
|
+
$('addrCustom').open = true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
594
682
|
/* Pinned GitHub OAuth-app values for a domain (auth.js WEB_REDIRECT_ORIGIN —
|
|
595
683
|
callback is the apex /api/gds/auth/web/callback). Mirrors onboard.oauthAppInstructions. */
|
|
596
684
|
function oauthAppValues(domain) {
|
|
@@ -633,6 +721,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
633
721
|
var h = $(panels[step]).querySelector('h1');
|
|
634
722
|
if (h) { h.setAttribute('tabindex', '-1'); h.focus({ preventScroll: false }); }
|
|
635
723
|
if (step === 2) { syncHome(); ensureRepoAccess(); }
|
|
724
|
+
if (step === 3) renderAddrPanel();
|
|
636
725
|
if (step === 4) renderReview();
|
|
637
726
|
}
|
|
638
727
|
|
|
@@ -642,6 +731,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
642
731
|
$('f-repo').value = state.repo;
|
|
643
732
|
$('f-slug').value = state.slug;
|
|
644
733
|
$('f-domain').value = state.domain;
|
|
734
|
+
$('f-noaddr').checked = !!state.noAddress;
|
|
645
735
|
|
|
646
736
|
/* Auto-fill the repo name from the project name until the visitor edits it
|
|
647
737
|
directly (mirrors the handle's auto-derive). The owner is filled once we know
|
|
@@ -826,6 +916,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
826
916
|
if (!state.slug) { h.innerHTML = ' '; h.className = 'hint'; this.classList.remove('bad'); }
|
|
827
917
|
else if (validSlug(state.slug)) { h.textContent = 'Nice — that works.'; h.className = 'hint ok'; this.classList.remove('bad'); }
|
|
828
918
|
else { h.textContent = '2–40 lowercase letters, numbers, or dashes; no leading/trailing/double dash.'; h.className = 'hint err'; this.classList.add('bad'); }
|
|
919
|
+
renderAddrPanel();
|
|
829
920
|
saveDraft();
|
|
830
921
|
});
|
|
831
922
|
$('f-domain').addEventListener('input', function () {
|
|
@@ -834,6 +925,12 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
834
925
|
if (!state.domain) { h.innerHTML = ' '; h.className = 'hint'; this.classList.remove('bad'); }
|
|
835
926
|
else if (looksLikeDomain(state.domain)) { h.textContent = 'Looks like a domain.'; h.className = 'hint ok'; this.classList.remove('bad'); }
|
|
836
927
|
else { h.textContent = 'That doesn’t look like a domain yet (e.g. mercury.example.com).'; h.className = 'hint err'; this.classList.add('bad'); }
|
|
928
|
+
renderAddrPanel();
|
|
929
|
+
saveDraft();
|
|
930
|
+
});
|
|
931
|
+
$('f-noaddr').addEventListener('change', function () {
|
|
932
|
+
state.noAddress = !!this.checked;
|
|
933
|
+
renderAddrPanel();
|
|
837
934
|
saveDraft();
|
|
838
935
|
});
|
|
839
936
|
|
|
@@ -865,6 +962,15 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
865
962
|
return false;
|
|
866
963
|
}
|
|
867
964
|
if (state.domain && !looksLikeDomain(state.domain)) return false;
|
|
965
|
+
/* no auto-assigned address (dotless host): going domainless must be an
|
|
966
|
+
EXPLICIT choice — a domain, or the opt-out box. Never silent. */
|
|
967
|
+
if (!resolvedDomain() && !state.noAddress) {
|
|
968
|
+
$('addrCustom').open = true;
|
|
969
|
+
var hd = $('h-domain');
|
|
970
|
+
hd.textContent = 'Add a domain you own — or tick “start without an address” to go without one.';
|
|
971
|
+
hd.className = 'hint err';
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
868
974
|
return true;
|
|
869
975
|
}
|
|
870
976
|
return true;
|
|
@@ -888,7 +994,9 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
888
994
|
['Starting from', state.mode === 'adopt' ? 'A repo you already have' : 'A fresh, empty repo'],
|
|
889
995
|
['Repository', '<code>' + esc(state.owner + '/' + state.repo) + '</code>'],
|
|
890
996
|
['Handle', '<code>' + esc(state.slug) + '</code>'],
|
|
891
|
-
['
|
|
997
|
+
['Address', resolvedDomain()
|
|
998
|
+
? '<code>' + esc(resolvedDomain()) + '</code>'
|
|
999
|
+
: '<em style="font-weight:600;color:var(--warn)">none — sign-in stays locked until it has one</em>'],
|
|
892
1000
|
['Shape', 'Standalone — your own repo, pinned platform core']
|
|
893
1001
|
];
|
|
894
1002
|
$('reviewList').innerHTML = rows.map(function (r) {
|
|
@@ -952,7 +1060,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
952
1060
|
body: JSON.stringify({
|
|
953
1061
|
slug: state.slug,
|
|
954
1062
|
hosting_shape: 'standalone',
|
|
955
|
-
domain:
|
|
1063
|
+
domain: resolvedDomain() || undefined,
|
|
956
1064
|
target_ref: state.owner + '/' + state.repo,
|
|
957
1065
|
/* LOAD-BEARING for the control-plane runner, not just for the checklist we
|
|
958
1066
|
render (task 1002562): 'adopt' makes it clone + layer onto the repo's
|
|
@@ -974,7 +1082,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
974
1082
|
}).then(function (data) {
|
|
975
1083
|
try {
|
|
976
1084
|
sessionStorage.setItem(CREATED_KEY, JSON.stringify({
|
|
977
|
-
id: data.instance && data.instance.id, slug: state.slug, domain:
|
|
1085
|
+
id: data.instance && data.instance.id, slug: state.slug, domain: resolvedDomain(),
|
|
978
1086
|
mode: state.mode, name: state.name, at: Date.now()
|
|
979
1087
|
}));
|
|
980
1088
|
sessionStorage.removeItem(DRAFT_KEY);
|
|
@@ -1016,10 +1124,12 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1016
1124
|
var FALLBACK_HUMAN_KEYS = ['github-repo', 'oauth-app', 'first-sign-in', 'enter-hall'];
|
|
1017
1125
|
|
|
1018
1126
|
function fetchPlan() {
|
|
1019
|
-
var domain =
|
|
1127
|
+
var domain = resolvedDomain() || '';
|
|
1020
1128
|
var key = state.mode + '|' + domain;
|
|
1021
1129
|
if (!planPromises[key]) {
|
|
1022
|
-
|
|
1130
|
+
// shape=standalone: this wizard only creates control-plane standalone
|
|
1131
|
+
// instances, and the plan is shape-filtered server-side (direction §7)
|
|
1132
|
+
planPromises[key] = fetch(API + '/provisioning/onboard-plan?mode=' + encodeURIComponent(state.mode) + '&shape=standalone' + (domain ? '&domain=' + encodeURIComponent(domain) : ''))
|
|
1023
1133
|
.then(function (res) { if (!res.ok) throw new Error('plan'); return res.json(); })
|
|
1024
1134
|
.then(function (d) { return (d && d.steps) || []; })
|
|
1025
1135
|
.catch(function () {
|
|
@@ -1048,7 +1158,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1048
1158
|
: '<li data-onboard-step="github-repo"><b>Your empty repo is ready</b><p>We created ' + repoLabel + ' on GitHub for you — public (projects here are open by design), empty, and waiting. The scaffold lands there when the build runs; nothing for you to do here.</p></li>';
|
|
1049
1159
|
}
|
|
1050
1160
|
var ghNewUrl = githubNewRepoUrl(state.owner, state.repo);
|
|
1051
|
-
return '<li' + (landed ? ' class="sdone"' : '') + ' data-onboard-step="github-repo"><b>Create the empty GitHub repo</b><p>Do this first — the build needs the repo to exist. Click below (GitHub opens with the name already filled in), press <strong>Create repository</strong>, and leave it completely empty. Seeing “repository not found” above? That just means the repo isn’t created yet — make it here.</p>' +
|
|
1161
|
+
return '<li' + (landed ? ' class="sdone"' : '') + ' data-onboard-step="github-repo"><b>Create the empty GitHub repo</b><p>Do this first — the build needs the repo to exist. Click below (GitHub opens with the name already filled in), press <strong>Create repository</strong>, and leave it completely empty. GitHub will ask public or private — your choice; both work here, and we never change it. Seeing “repository not found” above? That just means the repo isn’t created yet — make it here.</p>' +
|
|
1052
1162
|
'<a class="ghbtn" href="' + esc(ghNewUrl) + '" target="_blank" rel="noopener">' + GH_ICON + 'Create ' + repoLabel + ' on GitHub</a></li>';
|
|
1053
1163
|
}
|
|
1054
1164
|
|
|
@@ -1056,7 +1166,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1056
1166
|
control-plane placement is named as the operator's, never handed to the owner
|
|
1057
1167
|
as a command to run (gap 4). */
|
|
1058
1168
|
function manualOauthDetails() {
|
|
1059
|
-
var oa = oauthAppValues(
|
|
1169
|
+
var oa = oauthAppValues(resolvedDomain());
|
|
1060
1170
|
return '<details><summary>Prefer to create an OAuth app by hand?</summary>' +
|
|
1061
1171
|
'<p>GitHub → <em>your account’s</em> Settings → Developer settings → OAuth Apps → New OAuth App. (Developer settings live under your account, not the repo.)</p>' +
|
|
1062
1172
|
'<div class="kv"><span class="kk">Homepage URL</span><code>' + esc(oa.homepageUrl) + '</code>' + copyBtn(oa.homepageUrl) + '</div>' +
|
|
@@ -1068,11 +1178,18 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1068
1178
|
(gap 3 / idea 1000676); locked until a domain exists (the App needs the real
|
|
1069
1179
|
callback host — the server refuses domainless with domain_required). */
|
|
1070
1180
|
function renderStepOauthApp() {
|
|
1071
|
-
|
|
1181
|
+
var domain = resolvedDomain();
|
|
1182
|
+
if (!domain) {
|
|
1072
1183
|
return '<li class="slocked" data-onboard-step="oauth-app"><b>Wire GitHub sign-in</b><p>Unlocks once your project has an address — sign-in needs a real domain. Attach one any time from <a href="/projects">your projects page</a>.</p></li>';
|
|
1073
1184
|
}
|
|
1185
|
+
/* honesty about the venue (owner walk, 2026-08-12): GitHub rejects app
|
|
1186
|
+
manifests whose hook URL isn't publicly reachable, so a locally-hosted
|
|
1187
|
+
platform can't complete the one-click flow — say it BEFORE the bounce. */
|
|
1188
|
+
if (!apexBase()) {
|
|
1189
|
+
return '<li class="slocked" data-onboard-step="oauth-app"><b>Wire GitHub sign-in</b><p>This platform is running on a local address, and GitHub refuses app setups it can’t reach over the public internet — so the one-click flow can’t complete here. On a publicly-addressed platform this is one click.</p>' + manualOauthDetails() + '</li>';
|
|
1190
|
+
}
|
|
1074
1191
|
if (live.manifest === 'done') {
|
|
1075
|
-
return '<li class="sdone" data-onboard-step="oauth-app"><b>GitHub sign-in is wired</b><p>Credentials placed and verified — sign-in on <code style="font-family:var(--font-mono)">' + esc(
|
|
1192
|
+
return '<li class="sdone" data-onboard-step="oauth-app"><b>GitHub sign-in is wired</b><p>Credentials placed and verified — sign-in on <code style="font-family:var(--font-mono)">' + esc(domain) + '</code> is live.</p></li>';
|
|
1076
1193
|
}
|
|
1077
1194
|
if (live.manifest === 'error' || live.manifest === 'expired') {
|
|
1078
1195
|
return '<li data-onboard-step="oauth-app"><b>Wire GitHub sign-in</b><p>' +
|
|
@@ -1087,15 +1204,17 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1087
1204
|
}
|
|
1088
1205
|
|
|
1089
1206
|
function renderStepFirstSignIn() {
|
|
1090
|
-
|
|
1207
|
+
var domain = resolvedDomain();
|
|
1208
|
+
if (!domain) {
|
|
1091
1209
|
return '<li class="slocked" data-onboard-step="first-sign-in"><b>First sign-in</b><p>Unlocks with sign-in above — you’ll open your project’s address, sign in with GitHub once, and be seated as the owner (the founding Archon).</p></li>';
|
|
1092
1210
|
}
|
|
1093
|
-
return '<li data-onboard-step="first-sign-in"><b>First sign-in</b><p>Open <a href="https://' + esc(
|
|
1211
|
+
return '<li data-onboard-step="first-sign-in"><b>First sign-in</b><p>Open <a href="https://' + esc(domain) + '" target="_blank" rel="noopener"><code style="font-family:var(--font-mono)">' + esc(domain) + '</code></a> and sign in with GitHub once. That seats you as the owner — the founding Archon.</p></li>';
|
|
1094
1212
|
}
|
|
1095
1213
|
|
|
1096
1214
|
function renderStepEnterHall() {
|
|
1097
|
-
|
|
1098
|
-
|
|
1215
|
+
var domain = resolvedDomain();
|
|
1216
|
+
return domain
|
|
1217
|
+
? '<li data-onboard-step="enter-hall"><b>Enter your builders hall</b><p>Your project’s home — open <a href="https://' + esc(domain) + '/builders" target="_blank" rel="noopener"><code style="font-family:var(--font-mono)">' + esc(domain) + '/builders</code></a> and start building.</p></li>'
|
|
1099
1218
|
: '<li data-onboard-step="enter-hall"><b>Enter your builders hall</b><p>No address yet — run <code style="font-family:var(--font-mono)">bongos dev</code> in your repo checkout to start the local hall on your machine.</p></li>';
|
|
1100
1219
|
}
|
|
1101
1220
|
|
|
@@ -1106,6 +1225,12 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1106
1225
|
'enter-hall': renderStepEnterHall
|
|
1107
1226
|
};
|
|
1108
1227
|
|
|
1228
|
+
/* The newborn-instance welcome + account-nag suppression (direction §5.6,
|
|
1229
|
+
idea 1000701) is NOT implemented here by design: it lives on the CHILD
|
|
1230
|
+
instance's own surfaces (its hall greeting + needs roster), which this
|
|
1231
|
+
control-plane wizard cannot reach. Spun out per the direction's §9
|
|
1232
|
+
delivery-split as task 1002739. */
|
|
1233
|
+
|
|
1109
1234
|
function renderSteps() {
|
|
1110
1235
|
fetchPlan().then(function (steps) {
|
|
1111
1236
|
var html = steps.filter(function (s) { return s && s.kind === 'human'; }).map(function (s) {
|
|
@@ -1122,7 +1247,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1122
1247
|
var adopt = state.mode === 'adopt';
|
|
1123
1248
|
$('doneLede').innerHTML = 'Provisioning is requested for <strong>' + esc(state.name || state.slug) + '</strong>' +
|
|
1124
1249
|
(adopt ? ' — adopting your existing repo' : '') +
|
|
1125
|
-
'. The platform stands up the server, database, and address on its own —
|
|
1250
|
+
'. The platform stands up the server, database, and address on its own — watch it right here, or any time on <a href="/projects">your projects page</a>.';
|
|
1126
1251
|
renderSteps();
|
|
1127
1252
|
}
|
|
1128
1253
|
|
|
@@ -1215,9 +1340,9 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1215
1340
|
if (!el) return true;
|
|
1216
1341
|
var s = inst && inst.status;
|
|
1217
1342
|
if (s === 'requested') {
|
|
1218
|
-
el.innerHTML = progressMarkup(1, 1, 'Waiting for a
|
|
1343
|
+
el.innerHTML = progressMarkup(1, 1, 'Waiting for a runner to pick it up. This updates the moment it starts.');
|
|
1219
1344
|
} else if (s === 'provisioning') {
|
|
1220
|
-
el.innerHTML = progressMarkup(1, 1, 'Raising the server, database, and address
|
|
1345
|
+
el.innerHTML = progressMarkup(1, 1, 'Raising the server, database, and address — nothing needed from you.');
|
|
1221
1346
|
} else if (s === 'active') {
|
|
1222
1347
|
var addr = inst.domain ? 'https://' + inst.domain : null;
|
|
1223
1348
|
el.innerHTML = progressMarkup(-1, 3,
|
|
@@ -1225,11 +1350,13 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1225
1350
|
'. Finish the steps below and the hall is yours.', 'isLive');
|
|
1226
1351
|
return true;
|
|
1227
1352
|
} else if (s === 'error') {
|
|
1353
|
+
/* the honest snag (direction §5 / F19): no raw stderr as the headline, no
|
|
1354
|
+
false "keeps retrying" — retries stop, and the retry door is /projects */
|
|
1228
1355
|
el.innerHTML = progressMarkup(1, 1,
|
|
1229
|
-
'The standup
|
|
1230
|
-
'. The platform keeps retrying — this page updates as it recovers.', 'isErr');
|
|
1356
|
+
'The standup stopped partway — it won’t continue on its own. Retry it (and see the technical details) from <a href="/projects">your projects page</a>.', 'isErr');
|
|
1231
1357
|
} else if (s === 'tearing_down' || s === 'torn_down') {
|
|
1232
|
-
|
|
1358
|
+
/* a deliberate end is calm, never styled as a failure (direction §5) */
|
|
1359
|
+
el.innerHTML = progressMarkup(-1, 0, 'This instance was torn down. Bring it back any time from <a href="/projects">your projects page</a>, or start another below.');
|
|
1233
1360
|
return true;
|
|
1234
1361
|
} else {
|
|
1235
1362
|
el.innerHTML = progressMarkup(0, 1, 'Filed. Watching for the standup to start…');
|
|
@@ -1297,8 +1424,9 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1297
1424
|
live = { instStatus: null, manifest: null, manifestMsg: '' };
|
|
1298
1425
|
lastStepsHtml = '';
|
|
1299
1426
|
try { sessionStorage.removeItem(CREATED_KEY); sessionStorage.removeItem(DRAFT_KEY); } catch (e) {}
|
|
1300
|
-
state = { step: 1, name: '', mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '' };
|
|
1427
|
+
state = { step: 1, name: '', mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false };
|
|
1301
1428
|
$('f-name').value = ''; $('f-owner').value = ''; $('f-repo').value = ''; $('f-slug').value = ''; $('f-domain').value = '';
|
|
1429
|
+
$('f-noaddr').checked = false;
|
|
1302
1430
|
// Re-seed the owner from the signed-in account for the next project.
|
|
1303
1431
|
if (me && me.github_login) { state.owner = me.github_login; $('f-owner').value = state.owner; }
|
|
1304
1432
|
$('h-name').innerHTML = ' ';
|