@beastmode-develeap/beastmode 0.1.0
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/README.md +94 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9802 -0
- package/dist/index.js.map +1 -0
- package/dist/methodologies/bmad.json +40 -0
- package/dist/methodologies/lean-startup.json +32 -0
- package/dist/methodologies/traditional-prd.json +40 -0
- package/dist/web/board.html +5660 -0
- package/dist/web/develeap-icon.png +0 -0
- package/dist/web/develeap-logo.png +0 -0
- package/dist/web/favicon.svg +25 -0
- package/dist/web/index.html +1241 -0
- package/dist/web/logo-dark.svg +30 -0
- package/dist/web/logo-full.svg +48 -0
- package/dist/web/logo-horizontal.svg +46 -0
- package/dist/web/logo-icon.svg +53 -0
- package/dist/web/logo-light.svg +30 -0
- package/package.json +62 -0
|
@@ -0,0 +1,1241 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>BeastMode — Dark Factory Init</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
|
|
8
|
+
<!--INIT_DATA-->
|
|
9
|
+
<style>
|
|
10
|
+
:root {
|
|
11
|
+
--bg: #0a0a0a;
|
|
12
|
+
--bg-card: #141414;
|
|
13
|
+
--bg-input: #1a1a1a;
|
|
14
|
+
--bg-hover: #1f1f1f;
|
|
15
|
+
--border: #2a2a2a;
|
|
16
|
+
--border-focus: #F5A623;
|
|
17
|
+
--text: #f0f0f0;
|
|
18
|
+
--text-dim: #999;
|
|
19
|
+
--text-muted: #666;
|
|
20
|
+
--accent: #F5A623;
|
|
21
|
+
--accent-dim: #E5961A;
|
|
22
|
+
--accent-glow: rgba(245, 166, 35, 0.18);
|
|
23
|
+
--danger: #ff4444;
|
|
24
|
+
--warning: #ffaa00;
|
|
25
|
+
--radius: 8px;
|
|
26
|
+
--font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;
|
|
27
|
+
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
31
|
+
|
|
32
|
+
body {
|
|
33
|
+
font-family: var(--font-sans);
|
|
34
|
+
background: var(--bg);
|
|
35
|
+
color: var(--text);
|
|
36
|
+
min-height: 100vh;
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
align-items: center;
|
|
40
|
+
padding: 40px 20px;
|
|
41
|
+
line-height: 1.6;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.wizard {
|
|
45
|
+
width: 100%;
|
|
46
|
+
max-width: 720px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ── Header ── */
|
|
50
|
+
.wizard-header {
|
|
51
|
+
text-align: center;
|
|
52
|
+
margin-bottom: 40px;
|
|
53
|
+
}
|
|
54
|
+
.wizard-header h1 {
|
|
55
|
+
font-family: var(--font-mono);
|
|
56
|
+
font-size: 28px;
|
|
57
|
+
font-weight: 700;
|
|
58
|
+
color: var(--accent);
|
|
59
|
+
letter-spacing: -0.5px;
|
|
60
|
+
}
|
|
61
|
+
.wizard-logo {
|
|
62
|
+
width: 280px;
|
|
63
|
+
height: auto;
|
|
64
|
+
display: block;
|
|
65
|
+
margin: 0 auto;
|
|
66
|
+
}
|
|
67
|
+
.wizard-header p {
|
|
68
|
+
color: var(--text-dim);
|
|
69
|
+
margin-top: 8px;
|
|
70
|
+
font-size: 14px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ── Progress ── */
|
|
74
|
+
.progress {
|
|
75
|
+
display: flex;
|
|
76
|
+
justify-content: center;
|
|
77
|
+
gap: 8px;
|
|
78
|
+
margin-bottom: 40px;
|
|
79
|
+
position: relative;
|
|
80
|
+
}
|
|
81
|
+
.progress-step {
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 6px;
|
|
86
|
+
flex: 1;
|
|
87
|
+
max-width: 100px;
|
|
88
|
+
}
|
|
89
|
+
.progress-dot {
|
|
90
|
+
width: 32px;
|
|
91
|
+
height: 32px;
|
|
92
|
+
border-radius: 50%;
|
|
93
|
+
border: 2px solid var(--border);
|
|
94
|
+
display: flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
font-size: 13px;
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
color: var(--text-muted);
|
|
100
|
+
transition: all 0.3s ease;
|
|
101
|
+
position: relative;
|
|
102
|
+
z-index: 1;
|
|
103
|
+
}
|
|
104
|
+
.progress-dot.active {
|
|
105
|
+
border-color: var(--accent);
|
|
106
|
+
color: var(--accent);
|
|
107
|
+
box-shadow: 0 0 12px var(--accent-glow);
|
|
108
|
+
}
|
|
109
|
+
.progress-dot.done {
|
|
110
|
+
border-color: var(--accent);
|
|
111
|
+
background: var(--accent);
|
|
112
|
+
color: var(--bg);
|
|
113
|
+
}
|
|
114
|
+
.progress-label {
|
|
115
|
+
font-size: 11px;
|
|
116
|
+
color: var(--text-muted);
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
letter-spacing: 0.5px;
|
|
119
|
+
}
|
|
120
|
+
.progress-label.active { color: var(--accent); }
|
|
121
|
+
.progress-label.done { color: var(--text-dim); }
|
|
122
|
+
|
|
123
|
+
/* ── Step Card ── */
|
|
124
|
+
.step {
|
|
125
|
+
display: none;
|
|
126
|
+
background: var(--bg-card);
|
|
127
|
+
border: 1px solid var(--border);
|
|
128
|
+
border-radius: var(--radius);
|
|
129
|
+
padding: 32px;
|
|
130
|
+
animation: fadeIn 0.3s ease;
|
|
131
|
+
}
|
|
132
|
+
.step.active { display: block; }
|
|
133
|
+
.step h2 {
|
|
134
|
+
font-size: 20px;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
margin-bottom: 8px;
|
|
137
|
+
}
|
|
138
|
+
.step .subtitle {
|
|
139
|
+
color: var(--text-dim);
|
|
140
|
+
font-size: 14px;
|
|
141
|
+
margin-bottom: 24px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ── Forms ── */
|
|
145
|
+
.form-group {
|
|
146
|
+
margin-bottom: 20px;
|
|
147
|
+
}
|
|
148
|
+
.form-group label {
|
|
149
|
+
display: block;
|
|
150
|
+
font-size: 13px;
|
|
151
|
+
font-weight: 500;
|
|
152
|
+
color: var(--text-dim);
|
|
153
|
+
margin-bottom: 6px;
|
|
154
|
+
text-transform: uppercase;
|
|
155
|
+
letter-spacing: 0.5px;
|
|
156
|
+
}
|
|
157
|
+
input[type="text"],
|
|
158
|
+
input[type="password"],
|
|
159
|
+
input[type="number"],
|
|
160
|
+
select {
|
|
161
|
+
width: 100%;
|
|
162
|
+
padding: 10px 14px;
|
|
163
|
+
background: var(--bg-input);
|
|
164
|
+
border: 1px solid var(--border);
|
|
165
|
+
border-radius: var(--radius);
|
|
166
|
+
color: var(--text);
|
|
167
|
+
font-size: 14px;
|
|
168
|
+
font-family: var(--font-mono);
|
|
169
|
+
outline: none;
|
|
170
|
+
transition: border-color 0.2s;
|
|
171
|
+
}
|
|
172
|
+
input:focus, select:focus {
|
|
173
|
+
border-color: var(--border-focus);
|
|
174
|
+
box-shadow: 0 0 0 3px var(--accent-glow);
|
|
175
|
+
}
|
|
176
|
+
input::placeholder { color: var(--text-muted); }
|
|
177
|
+
|
|
178
|
+
/* ── Radio Cards ── */
|
|
179
|
+
.radio-cards {
|
|
180
|
+
display: grid;
|
|
181
|
+
gap: 10px;
|
|
182
|
+
}
|
|
183
|
+
.radio-card {
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: flex-start;
|
|
186
|
+
gap: 12px;
|
|
187
|
+
padding: 14px 16px;
|
|
188
|
+
background: var(--bg-input);
|
|
189
|
+
border: 1px solid var(--border);
|
|
190
|
+
border-radius: var(--radius);
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
transition: all 0.2s;
|
|
193
|
+
}
|
|
194
|
+
.radio-card:hover { border-color: var(--text-muted); background: var(--bg-hover); }
|
|
195
|
+
.radio-card.selected { border-color: var(--accent); background: var(--accent-glow); }
|
|
196
|
+
.radio-card input[type="radio"] { display: none; }
|
|
197
|
+
.radio-indicator {
|
|
198
|
+
width: 18px;
|
|
199
|
+
height: 18px;
|
|
200
|
+
border-radius: 50%;
|
|
201
|
+
border: 2px solid var(--border);
|
|
202
|
+
flex-shrink: 0;
|
|
203
|
+
margin-top: 2px;
|
|
204
|
+
display: flex;
|
|
205
|
+
align-items: center;
|
|
206
|
+
justify-content: center;
|
|
207
|
+
transition: all 0.2s;
|
|
208
|
+
}
|
|
209
|
+
.radio-card.selected .radio-indicator {
|
|
210
|
+
border-color: var(--accent);
|
|
211
|
+
}
|
|
212
|
+
.radio-card.selected .radio-indicator::after {
|
|
213
|
+
content: '';
|
|
214
|
+
width: 8px;
|
|
215
|
+
height: 8px;
|
|
216
|
+
border-radius: 50%;
|
|
217
|
+
background: var(--accent);
|
|
218
|
+
}
|
|
219
|
+
.radio-info h4 { font-size: 14px; font-weight: 600; }
|
|
220
|
+
.radio-info p { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
|
|
221
|
+
.radio-info .badge {
|
|
222
|
+
display: inline-block;
|
|
223
|
+
font-size: 10px;
|
|
224
|
+
padding: 2px 8px;
|
|
225
|
+
border-radius: 10px;
|
|
226
|
+
background: var(--accent-glow);
|
|
227
|
+
color: var(--accent);
|
|
228
|
+
font-weight: 600;
|
|
229
|
+
margin-left: 8px;
|
|
230
|
+
text-transform: uppercase;
|
|
231
|
+
letter-spacing: 0.5px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* ── Detected Info ── */
|
|
235
|
+
.detect-grid {
|
|
236
|
+
display: grid;
|
|
237
|
+
grid-template-columns: 1fr 1fr;
|
|
238
|
+
gap: 12px;
|
|
239
|
+
margin-bottom: 20px;
|
|
240
|
+
}
|
|
241
|
+
.detect-item {
|
|
242
|
+
padding: 12px 14px;
|
|
243
|
+
background: var(--bg-input);
|
|
244
|
+
border: 1px solid var(--border);
|
|
245
|
+
border-radius: var(--radius);
|
|
246
|
+
}
|
|
247
|
+
.detect-item .detect-label {
|
|
248
|
+
font-size: 11px;
|
|
249
|
+
color: var(--text-muted);
|
|
250
|
+
text-transform: uppercase;
|
|
251
|
+
letter-spacing: 0.5px;
|
|
252
|
+
}
|
|
253
|
+
.detect-item .detect-value {
|
|
254
|
+
font-family: var(--font-mono);
|
|
255
|
+
font-size: 14px;
|
|
256
|
+
color: var(--accent);
|
|
257
|
+
margin-top: 4px;
|
|
258
|
+
}
|
|
259
|
+
.command-list {
|
|
260
|
+
margin-bottom: 20px;
|
|
261
|
+
}
|
|
262
|
+
.command-row {
|
|
263
|
+
display: flex;
|
|
264
|
+
align-items: center;
|
|
265
|
+
padding: 8px 14px;
|
|
266
|
+
border-bottom: 1px solid var(--border);
|
|
267
|
+
font-family: var(--font-mono);
|
|
268
|
+
font-size: 13px;
|
|
269
|
+
}
|
|
270
|
+
.command-row:last-child { border-bottom: none; }
|
|
271
|
+
.command-label {
|
|
272
|
+
width: 80px;
|
|
273
|
+
color: var(--text-muted);
|
|
274
|
+
font-size: 12px;
|
|
275
|
+
flex-shrink: 0;
|
|
276
|
+
}
|
|
277
|
+
.command-value { color: var(--text); }
|
|
278
|
+
|
|
279
|
+
/* ── Plugin Cards ── */
|
|
280
|
+
.plugin-cards {
|
|
281
|
+
display: grid;
|
|
282
|
+
gap: 10px;
|
|
283
|
+
}
|
|
284
|
+
.plugin-card {
|
|
285
|
+
display: flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
justify-content: space-between;
|
|
288
|
+
padding: 14px 16px;
|
|
289
|
+
background: var(--bg-input);
|
|
290
|
+
border: 1px solid var(--border);
|
|
291
|
+
border-radius: var(--radius);
|
|
292
|
+
}
|
|
293
|
+
.plugin-name { font-weight: 600; font-size: 14px; }
|
|
294
|
+
.plugin-desc { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
|
|
295
|
+
.badge-soon {
|
|
296
|
+
font-size: 10px;
|
|
297
|
+
padding: 3px 10px;
|
|
298
|
+
border-radius: 10px;
|
|
299
|
+
background: var(--bg-hover);
|
|
300
|
+
color: var(--text-muted);
|
|
301
|
+
border: 1px solid var(--border);
|
|
302
|
+
text-transform: uppercase;
|
|
303
|
+
letter-spacing: 0.5px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* ── Secret Fields ── */
|
|
307
|
+
.secret-field {
|
|
308
|
+
display: flex;
|
|
309
|
+
align-items: center;
|
|
310
|
+
gap: 12px;
|
|
311
|
+
padding: 12px 14px;
|
|
312
|
+
background: var(--bg-input);
|
|
313
|
+
border: 1px solid var(--border);
|
|
314
|
+
border-radius: var(--radius);
|
|
315
|
+
margin-bottom: 10px;
|
|
316
|
+
}
|
|
317
|
+
.secret-field .secret-status {
|
|
318
|
+
width: 10px;
|
|
319
|
+
height: 10px;
|
|
320
|
+
border-radius: 50%;
|
|
321
|
+
flex-shrink: 0;
|
|
322
|
+
}
|
|
323
|
+
.secret-field .secret-status.present { background: var(--accent); }
|
|
324
|
+
.secret-field .secret-status.missing { background: var(--text-muted); }
|
|
325
|
+
.secret-field .secret-info { flex: 1; }
|
|
326
|
+
.secret-field .secret-name {
|
|
327
|
+
font-family: var(--font-mono);
|
|
328
|
+
font-size: 13px;
|
|
329
|
+
font-weight: 600;
|
|
330
|
+
}
|
|
331
|
+
.secret-field .secret-reason {
|
|
332
|
+
font-size: 11px;
|
|
333
|
+
color: var(--text-dim);
|
|
334
|
+
}
|
|
335
|
+
.secret-field input[type="password"] {
|
|
336
|
+
width: 200px;
|
|
337
|
+
flex-shrink: 0;
|
|
338
|
+
}
|
|
339
|
+
.secret-field .found-label {
|
|
340
|
+
font-size: 12px;
|
|
341
|
+
color: var(--accent);
|
|
342
|
+
font-weight: 500;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* ── Buttons ── */
|
|
346
|
+
.nav-buttons {
|
|
347
|
+
display: flex;
|
|
348
|
+
justify-content: space-between;
|
|
349
|
+
margin-top: 32px;
|
|
350
|
+
padding-top: 20px;
|
|
351
|
+
border-top: 1px solid var(--border);
|
|
352
|
+
}
|
|
353
|
+
.btn {
|
|
354
|
+
padding: 10px 24px;
|
|
355
|
+
border-radius: var(--radius);
|
|
356
|
+
border: 1px solid var(--border);
|
|
357
|
+
background: var(--bg-input);
|
|
358
|
+
color: var(--text);
|
|
359
|
+
font-size: 14px;
|
|
360
|
+
font-weight: 500;
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
transition: all 0.2s;
|
|
363
|
+
}
|
|
364
|
+
.btn:hover { background: var(--bg-hover); border-color: var(--text-muted); }
|
|
365
|
+
.btn:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
366
|
+
.btn-primary {
|
|
367
|
+
background: var(--accent);
|
|
368
|
+
color: var(--bg);
|
|
369
|
+
border-color: var(--accent);
|
|
370
|
+
font-weight: 600;
|
|
371
|
+
}
|
|
372
|
+
.btn-primary:hover { background: var(--accent-dim); }
|
|
373
|
+
.btn-primary:disabled { background: var(--accent-dim); border-color: var(--accent-dim); }
|
|
374
|
+
|
|
375
|
+
.toggle-link {
|
|
376
|
+
color: var(--accent);
|
|
377
|
+
font-size: 13px;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
background: none;
|
|
380
|
+
border: none;
|
|
381
|
+
text-decoration: underline;
|
|
382
|
+
font-family: inherit;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.override-form { display: none; margin-top: 16px; }
|
|
386
|
+
.override-form.visible { display: block; }
|
|
387
|
+
|
|
388
|
+
.loading {
|
|
389
|
+
display: flex;
|
|
390
|
+
align-items: center;
|
|
391
|
+
gap: 12px;
|
|
392
|
+
padding: 20px 0;
|
|
393
|
+
color: var(--text-dim);
|
|
394
|
+
}
|
|
395
|
+
.spinner {
|
|
396
|
+
width: 20px;
|
|
397
|
+
height: 20px;
|
|
398
|
+
border: 2px solid var(--border);
|
|
399
|
+
border-top-color: var(--accent);
|
|
400
|
+
border-radius: 50%;
|
|
401
|
+
animation: spin 0.8s linear infinite;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.empty-state {
|
|
405
|
+
text-align: center;
|
|
406
|
+
padding: 24px;
|
|
407
|
+
color: var(--text-muted);
|
|
408
|
+
font-size: 14px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* ── Boot Animation ── */
|
|
412
|
+
.boot-screen {
|
|
413
|
+
display: none;
|
|
414
|
+
background: var(--bg);
|
|
415
|
+
min-height: 400px;
|
|
416
|
+
border-radius: var(--radius);
|
|
417
|
+
padding: 32px;
|
|
418
|
+
font-family: var(--font-mono);
|
|
419
|
+
overflow: hidden;
|
|
420
|
+
}
|
|
421
|
+
.boot-screen.active { display: block; }
|
|
422
|
+
|
|
423
|
+
.boot-cursor {
|
|
424
|
+
display: inline-block;
|
|
425
|
+
width: 8px;
|
|
426
|
+
height: 18px;
|
|
427
|
+
background: var(--accent);
|
|
428
|
+
animation: blink 1s step-end infinite;
|
|
429
|
+
vertical-align: middle;
|
|
430
|
+
margin-left: 2px;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.boot-line {
|
|
434
|
+
opacity: 0;
|
|
435
|
+
transform: translateY(8px);
|
|
436
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
437
|
+
padding: 2px 0;
|
|
438
|
+
font-size: 13px;
|
|
439
|
+
color: var(--text-dim);
|
|
440
|
+
}
|
|
441
|
+
.boot-line.visible {
|
|
442
|
+
opacity: 1;
|
|
443
|
+
transform: translateY(0);
|
|
444
|
+
}
|
|
445
|
+
.boot-line .checkmark {
|
|
446
|
+
color: var(--accent);
|
|
447
|
+
display: inline-block;
|
|
448
|
+
transform: scale(0);
|
|
449
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
450
|
+
margin-right: 8px;
|
|
451
|
+
}
|
|
452
|
+
.boot-line .checkmark.visible { transform: scale(1); }
|
|
453
|
+
.boot-line.file-line { padding-left: 20px; }
|
|
454
|
+
|
|
455
|
+
.boot-title {
|
|
456
|
+
font-size: 24px;
|
|
457
|
+
font-weight: 700;
|
|
458
|
+
color: var(--accent);
|
|
459
|
+
opacity: 0;
|
|
460
|
+
transition: opacity 0.6s ease;
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
white-space: nowrap;
|
|
463
|
+
border-right: 3px solid var(--accent);
|
|
464
|
+
width: 0;
|
|
465
|
+
display: inline-block;
|
|
466
|
+
}
|
|
467
|
+
.boot-title.typing {
|
|
468
|
+
animation: typewriter 0.8s steps(20) forwards, cursorBlink 0.6s step-end 3;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.boot-config-section {
|
|
472
|
+
margin: 12px 0;
|
|
473
|
+
opacity: 0;
|
|
474
|
+
transform: translateY(12px);
|
|
475
|
+
transition: all 0.4s ease;
|
|
476
|
+
}
|
|
477
|
+
.boot-config-section.visible {
|
|
478
|
+
opacity: 1;
|
|
479
|
+
transform: translateY(0);
|
|
480
|
+
}
|
|
481
|
+
.boot-config-section .section-label {
|
|
482
|
+
color: var(--accent);
|
|
483
|
+
font-size: 12px;
|
|
484
|
+
text-transform: uppercase;
|
|
485
|
+
letter-spacing: 1px;
|
|
486
|
+
}
|
|
487
|
+
.boot-config-section .section-value {
|
|
488
|
+
color: var(--text);
|
|
489
|
+
font-size: 13px;
|
|
490
|
+
margin-top: 2px;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.boot-status-dot {
|
|
494
|
+
display: inline-block;
|
|
495
|
+
width: 8px;
|
|
496
|
+
height: 8px;
|
|
497
|
+
border-radius: 50%;
|
|
498
|
+
margin-right: 8px;
|
|
499
|
+
transform: scale(0);
|
|
500
|
+
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
501
|
+
}
|
|
502
|
+
.boot-status-dot.visible { transform: scale(1); }
|
|
503
|
+
.boot-status-dot.green { background: var(--accent); }
|
|
504
|
+
.boot-status-dot.amber { background: var(--warning); }
|
|
505
|
+
|
|
506
|
+
.boot-ready {
|
|
507
|
+
text-align: center;
|
|
508
|
+
margin-top: 32px;
|
|
509
|
+
opacity: 0;
|
|
510
|
+
transition: opacity 0.6s ease;
|
|
511
|
+
}
|
|
512
|
+
.boot-ready.visible { opacity: 1; }
|
|
513
|
+
.boot-ready h2 {
|
|
514
|
+
font-size: 28px;
|
|
515
|
+
color: var(--accent);
|
|
516
|
+
font-weight: 700;
|
|
517
|
+
text-shadow: 0 0 30px var(--accent-glow);
|
|
518
|
+
margin-bottom: 16px;
|
|
519
|
+
}
|
|
520
|
+
.boot-ready .next-steps {
|
|
521
|
+
text-align: left;
|
|
522
|
+
display: inline-block;
|
|
523
|
+
}
|
|
524
|
+
.boot-ready .next-step {
|
|
525
|
+
display: flex;
|
|
526
|
+
align-items: center;
|
|
527
|
+
gap: 10px;
|
|
528
|
+
margin: 8px 0;
|
|
529
|
+
font-size: 13px;
|
|
530
|
+
}
|
|
531
|
+
.boot-ready .next-step code {
|
|
532
|
+
background: var(--bg-input);
|
|
533
|
+
padding: 4px 10px;
|
|
534
|
+
border-radius: 4px;
|
|
535
|
+
border: 1px solid var(--border);
|
|
536
|
+
font-family: var(--font-mono);
|
|
537
|
+
color: var(--accent);
|
|
538
|
+
cursor: pointer;
|
|
539
|
+
transition: background 0.2s;
|
|
540
|
+
}
|
|
541
|
+
.boot-ready .next-step code:hover { background: var(--bg-hover); }
|
|
542
|
+
.boot-ready .next-step .step-desc {
|
|
543
|
+
color: var(--text-dim);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/* ── Animations ── */
|
|
547
|
+
@keyframes fadeIn {
|
|
548
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
549
|
+
to { opacity: 1; transform: translateY(0); }
|
|
550
|
+
}
|
|
551
|
+
@keyframes spin {
|
|
552
|
+
to { transform: rotate(360deg); }
|
|
553
|
+
}
|
|
554
|
+
@keyframes blink {
|
|
555
|
+
50% { opacity: 0; }
|
|
556
|
+
}
|
|
557
|
+
@keyframes typewriter {
|
|
558
|
+
from { width: 0; }
|
|
559
|
+
to { width: 100%; }
|
|
560
|
+
}
|
|
561
|
+
@keyframes cursorBlink {
|
|
562
|
+
50% { border-color: transparent; }
|
|
563
|
+
}
|
|
564
|
+
</style>
|
|
565
|
+
</head>
|
|
566
|
+
<body>
|
|
567
|
+
|
|
568
|
+
<div class="wizard">
|
|
569
|
+
<div class="wizard-header">
|
|
570
|
+
<svg class="wizard-logo" viewBox="0 0 520 80" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="BeastMode — Dark Factory">
|
|
571
|
+
<g transform="translate(45,48) scale(0.3)">
|
|
572
|
+
<rect fill="#f0f0f0" x="-52" y="8" width="62" height="56" rx="2"/>
|
|
573
|
+
<rect fill="#f0f0f0" x="12" y="-12" width="44" height="76" rx="2"/>
|
|
574
|
+
<rect fill="#f0f0f0" x="20" y="-50" width="13" height="42" rx="1.5"/>
|
|
575
|
+
<rect fill="#f0f0f0" x="39" y="-32" width="10" height="24" rx="1.5"/>
|
|
576
|
+
<rect fill="#141414" x="-43" y="18" width="10" height="9" rx="1"/>
|
|
577
|
+
<rect fill="#141414" x="-28" y="18" width="10" height="9" rx="1"/>
|
|
578
|
+
<rect fill="#141414" x="-43" y="34" width="10" height="9" rx="1"/>
|
|
579
|
+
<rect fill="#141414" x="-28" y="34" width="10" height="9" rx="1"/>
|
|
580
|
+
<rect fill="#141414" x="20" y="0" width="10" height="10" rx="1"/>
|
|
581
|
+
<rect fill="#141414" x="38" y="0" width="10" height="10" rx="1"/>
|
|
582
|
+
<rect fill="#141414" x="20" y="18" width="10" height="10" rx="1"/>
|
|
583
|
+
<rect fill="#141414" x="38" y="18" width="10" height="10" rx="1"/>
|
|
584
|
+
<rect fill="#f0f0f0" x="-58" y="64" width="120" height="3" rx="1.5"/>
|
|
585
|
+
<line stroke="#F5A623" fill="none" x1="26" y1="-54" x2="26" y2="-90" stroke-width="3.5" stroke-linecap="round"/>
|
|
586
|
+
<polygon fill="#F5A623" points="26,-90 18,-76 34,-76"/>
|
|
587
|
+
<line stroke="#F5A623" fill="none" x1="14" y1="-56" x2="6" y2="-76" stroke-width="1.8" stroke-linecap="round"/>
|
|
588
|
+
<polygon fill="#F5A623" points="6,-76 1,-67 11,-67"/>
|
|
589
|
+
<line stroke="#F5A623" fill="none" x1="40" y1="-46" x2="47" y2="-68" stroke-width="1.8" stroke-linecap="round"/>
|
|
590
|
+
<polygon fill="#F5A623" points="47,-68 42,-59 52,-59"/>
|
|
591
|
+
<circle fill="#F5A623" cx="26" cy="-98" r="2.5"/>
|
|
592
|
+
<circle fill="#F5A623" cx="0" cy="-82" r="1.5"/>
|
|
593
|
+
<circle fill="#F5A623" cx="52" cy="-74" r="1.5"/>
|
|
594
|
+
</g>
|
|
595
|
+
<text fill="#f0f0f0" x="90" y="44" font-family="system-ui,-apple-system,sans-serif" font-size="26" font-weight="500" letter-spacing="6">BEASTMODE</text>
|
|
596
|
+
<text fill="#555" x="90" y="62" font-family="system-ui,-apple-system,sans-serif" font-size="9" letter-spacing="3">DARK FACTORY</text>
|
|
597
|
+
</svg>
|
|
598
|
+
<p>Dark Factory Init Wizard</p>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
<div class="progress" id="progress">
|
|
602
|
+
<div class="progress-step">
|
|
603
|
+
<div class="progress-dot active" data-step="0">1</div>
|
|
604
|
+
<span class="progress-label active">Welcome</span>
|
|
605
|
+
</div>
|
|
606
|
+
<div class="progress-step">
|
|
607
|
+
<div class="progress-dot" data-step="1">2</div>
|
|
608
|
+
<span class="progress-label">Detect</span>
|
|
609
|
+
</div>
|
|
610
|
+
<div class="progress-step">
|
|
611
|
+
<div class="progress-dot" data-step="2">3</div>
|
|
612
|
+
<span class="progress-label">Configure</span>
|
|
613
|
+
</div>
|
|
614
|
+
<div class="progress-step">
|
|
615
|
+
<div class="progress-dot" data-step="3">4</div>
|
|
616
|
+
<span class="progress-label">Extend</span>
|
|
617
|
+
</div>
|
|
618
|
+
<div class="progress-step">
|
|
619
|
+
<div class="progress-dot" data-step="4">5</div>
|
|
620
|
+
<span class="progress-label">Connect</span>
|
|
621
|
+
</div>
|
|
622
|
+
<div class="progress-step">
|
|
623
|
+
<div class="progress-dot" data-step="5">6</div>
|
|
624
|
+
<span class="progress-label">Boot</span>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
|
|
628
|
+
<!-- ════════ Step 0: Welcome ════════ -->
|
|
629
|
+
<div class="step active" id="step-0">
|
|
630
|
+
<h2>Create Your Dark Factory</h2>
|
|
631
|
+
<p class="subtitle">A factory turns natural language intent into verified software. Let's set one up.</p>
|
|
632
|
+
|
|
633
|
+
<div class="form-group">
|
|
634
|
+
<label for="factory-name">Factory Name</label>
|
|
635
|
+
<input type="text" id="factory-name" placeholder="my-factory" />
|
|
636
|
+
</div>
|
|
637
|
+
|
|
638
|
+
<div class="form-group">
|
|
639
|
+
<label for="project-path">Project Path</label>
|
|
640
|
+
<input type="text" id="project-path" placeholder="/path/to/your/project" />
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
<div class="nav-buttons">
|
|
644
|
+
<div></div>
|
|
645
|
+
<button class="btn btn-primary" id="btn-next-0">Next</button>
|
|
646
|
+
</div>
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<!-- ════════ Step 1: Detect ════════ -->
|
|
650
|
+
<div class="step" id="step-1">
|
|
651
|
+
<h2>Stack Detection</h2>
|
|
652
|
+
<p class="subtitle">Analyzing your project to detect framework, language, and tooling.</p>
|
|
653
|
+
|
|
654
|
+
<div id="detect-loading" class="loading">
|
|
655
|
+
<div class="spinner"></div>
|
|
656
|
+
<span>Scanning project...</span>
|
|
657
|
+
</div>
|
|
658
|
+
|
|
659
|
+
<div id="detect-results" style="display:none">
|
|
660
|
+
<div class="detect-grid" id="detect-grid"></div>
|
|
661
|
+
|
|
662
|
+
<h3 style="font-size:14px; color:var(--text-dim); margin-bottom:8px; text-transform:uppercase; letter-spacing:0.5px;">Suggested Commands</h3>
|
|
663
|
+
<div class="command-list" id="command-list"></div>
|
|
664
|
+
|
|
665
|
+
<button class="toggle-link" id="toggle-overrides">Edit detected values</button>
|
|
666
|
+
<div class="override-form" id="override-form">
|
|
667
|
+
<div class="form-group">
|
|
668
|
+
<label for="override-build">Build Command</label>
|
|
669
|
+
<input type="text" id="override-build" />
|
|
670
|
+
</div>
|
|
671
|
+
<div class="form-group">
|
|
672
|
+
<label for="override-dev">Dev Command</label>
|
|
673
|
+
<input type="text" id="override-dev" />
|
|
674
|
+
</div>
|
|
675
|
+
<div class="form-group">
|
|
676
|
+
<label for="override-test">Test Command</label>
|
|
677
|
+
<input type="text" id="override-test" />
|
|
678
|
+
</div>
|
|
679
|
+
<div class="form-group">
|
|
680
|
+
<label for="override-install">Install Command</label>
|
|
681
|
+
<input type="text" id="override-install" />
|
|
682
|
+
</div>
|
|
683
|
+
<div class="form-group">
|
|
684
|
+
<label for="override-port">Dev Port</label>
|
|
685
|
+
<input type="number" id="override-port" />
|
|
686
|
+
</div>
|
|
687
|
+
</div>
|
|
688
|
+
</div>
|
|
689
|
+
|
|
690
|
+
<div id="detect-error" style="display:none; color:var(--danger); padding:16px 0;"></div>
|
|
691
|
+
|
|
692
|
+
<div class="nav-buttons">
|
|
693
|
+
<button class="btn" id="btn-prev-1">Previous</button>
|
|
694
|
+
<button class="btn btn-primary" id="btn-next-1" disabled>Next</button>
|
|
695
|
+
</div>
|
|
696
|
+
</div>
|
|
697
|
+
|
|
698
|
+
<!-- ════════ Step 2: Configure ════════ -->
|
|
699
|
+
<div class="step" id="step-2">
|
|
700
|
+
<h2>Pipeline Configuration</h2>
|
|
701
|
+
<p class="subtitle">Choose your pipeline preset, task backend, and deployment target.</p>
|
|
702
|
+
|
|
703
|
+
<div class="form-group">
|
|
704
|
+
<label>Pipeline Preset</label>
|
|
705
|
+
<div class="radio-cards" id="preset-cards"></div>
|
|
706
|
+
</div>
|
|
707
|
+
|
|
708
|
+
<div class="form-group">
|
|
709
|
+
<label for="backend-select">Task Backend</label>
|
|
710
|
+
<select id="backend-select"></select>
|
|
711
|
+
</div>
|
|
712
|
+
|
|
713
|
+
<div class="form-group">
|
|
714
|
+
<label for="deploy-select">Deploy Target</label>
|
|
715
|
+
<select id="deploy-select"></select>
|
|
716
|
+
</div>
|
|
717
|
+
|
|
718
|
+
<div class="nav-buttons">
|
|
719
|
+
<button class="btn" id="btn-prev-2">Previous</button>
|
|
720
|
+
<button class="btn btn-primary" id="btn-next-2">Next</button>
|
|
721
|
+
</div>
|
|
722
|
+
</div>
|
|
723
|
+
|
|
724
|
+
<!-- ════════ Step 3: Extend ════════ -->
|
|
725
|
+
<div class="step" id="step-3">
|
|
726
|
+
<h2>Extensions</h2>
|
|
727
|
+
<p class="subtitle">Suggested plugins for your detected stack. Plugin installation coming soon.</p>
|
|
728
|
+
|
|
729
|
+
<div class="plugin-cards" id="plugin-cards"></div>
|
|
730
|
+
<div id="no-plugins" class="empty-state" style="display:none">
|
|
731
|
+
No plugins suggested for your stack. You can add plugins later with <code style="background:var(--bg-input);padding:2px 6px;border-radius:4px;font-family:var(--font-mono);color:var(--accent)">beastmode add plugin</code>.
|
|
732
|
+
</div>
|
|
733
|
+
|
|
734
|
+
<div class="nav-buttons">
|
|
735
|
+
<button class="btn" id="btn-prev-3">Previous</button>
|
|
736
|
+
<button class="btn btn-primary" id="btn-next-3">Next</button>
|
|
737
|
+
</div>
|
|
738
|
+
</div>
|
|
739
|
+
|
|
740
|
+
<!-- ════════ Step 4: Connect ════════ -->
|
|
741
|
+
<div class="step" id="step-4">
|
|
742
|
+
<h2>Connect</h2>
|
|
743
|
+
<p class="subtitle">AI runs via Claude Code CLI (<code>claude login</code>). Only GitHub token is required. Secrets found in your environment are auto-detected.</p>
|
|
744
|
+
|
|
745
|
+
<div id="secrets-loading" class="loading">
|
|
746
|
+
<div class="spinner"></div>
|
|
747
|
+
<span>Checking environment...</span>
|
|
748
|
+
</div>
|
|
749
|
+
|
|
750
|
+
<div id="secrets-list" style="display:none"></div>
|
|
751
|
+
|
|
752
|
+
<div class="nav-buttons">
|
|
753
|
+
<button class="btn" id="btn-prev-4">Previous</button>
|
|
754
|
+
<button class="btn btn-primary" id="btn-next-4">Boot Factory</button>
|
|
755
|
+
</div>
|
|
756
|
+
</div>
|
|
757
|
+
|
|
758
|
+
<!-- ════════ Step 5: Boot ════════ -->
|
|
759
|
+
<div class="step" id="step-5">
|
|
760
|
+
<div class="boot-screen active" id="boot-screen">
|
|
761
|
+
<div id="boot-cursor-line"><span class="boot-cursor"></span></div>
|
|
762
|
+
<div id="boot-title-container" style="display:none">
|
|
763
|
+
<span class="boot-title" id="boot-title"></span>
|
|
764
|
+
</div>
|
|
765
|
+
<div id="boot-config" style="display:none"></div>
|
|
766
|
+
<div id="boot-files" style="display:none"></div>
|
|
767
|
+
<div id="boot-connections" style="display:none"></div>
|
|
768
|
+
<div class="boot-ready" id="boot-ready">
|
|
769
|
+
<h2>Factory Ready!</h2>
|
|
770
|
+
<div class="next-steps">
|
|
771
|
+
<div class="next-step">
|
|
772
|
+
<code id="cmd-board">cd <span class="factory-name-slot"></span> && beastmode board</code>
|
|
773
|
+
<span class="step-desc">Open the web board</span>
|
|
774
|
+
</div>
|
|
775
|
+
<div class="next-step">
|
|
776
|
+
<code id="cmd-run">cd <span class="factory-name-slot"></span> && beastmode run</code>
|
|
777
|
+
<span class="step-desc">Run pipeline locally</span>
|
|
778
|
+
</div>
|
|
779
|
+
<div class="next-step">
|
|
780
|
+
<code id="cmd-daemon">cd <span class="factory-name-slot"></span> && beastmode daemon</code>
|
|
781
|
+
<span class="step-desc">Start the daemon</span>
|
|
782
|
+
</div>
|
|
783
|
+
</div>
|
|
784
|
+
</div>
|
|
785
|
+
</div>
|
|
786
|
+
</div>
|
|
787
|
+
</div>
|
|
788
|
+
|
|
789
|
+
<script>
|
|
790
|
+
(function() {
|
|
791
|
+
'use strict';
|
|
792
|
+
|
|
793
|
+
// ── State ──
|
|
794
|
+
let currentStep = 0;
|
|
795
|
+
let stackInfo = null;
|
|
796
|
+
let selectedPreset = '';
|
|
797
|
+
let selectedBackend = 'beastmode-board';
|
|
798
|
+
let selectedDeploy = 'pr-only';
|
|
799
|
+
let secretReport = null;
|
|
800
|
+
let presetsData = [];
|
|
801
|
+
|
|
802
|
+
// ── Init data from server ──
|
|
803
|
+
const initData = window.__INIT_DATA__ || {};
|
|
804
|
+
if (initData.factoryName) {
|
|
805
|
+
document.getElementById('factory-name').value = initData.factoryName;
|
|
806
|
+
}
|
|
807
|
+
if (initData.projectPath) {
|
|
808
|
+
document.getElementById('project-path').value = initData.projectPath;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// ── API helpers ──
|
|
812
|
+
async function api(method, path, body) {
|
|
813
|
+
const opts = { method, headers: { 'Content-Type': 'application/json' } };
|
|
814
|
+
if (body) opts.body = JSON.stringify(body);
|
|
815
|
+
const res = await fetch(path, opts);
|
|
816
|
+
const data = await res.json();
|
|
817
|
+
if (!res.ok) throw new Error(data.error || 'Request failed');
|
|
818
|
+
return data;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// ── Navigation ──
|
|
822
|
+
function goToStep(n) {
|
|
823
|
+
if (n < 0 || n > 5) return;
|
|
824
|
+
document.querySelectorAll('.step').forEach((el, i) => {
|
|
825
|
+
el.classList.toggle('active', i === n);
|
|
826
|
+
});
|
|
827
|
+
document.querySelectorAll('.progress-dot').forEach((el, i) => {
|
|
828
|
+
el.classList.remove('active', 'done');
|
|
829
|
+
if (i < n) { el.classList.add('done'); el.textContent = '\u2713'; }
|
|
830
|
+
else if (i === n) el.classList.add('active');
|
|
831
|
+
else el.textContent = String(i + 1);
|
|
832
|
+
});
|
|
833
|
+
document.querySelectorAll('.progress-label').forEach((el, i) => {
|
|
834
|
+
el.classList.remove('active', 'done');
|
|
835
|
+
if (i < n) el.classList.add('done');
|
|
836
|
+
else if (i === n) el.classList.add('active');
|
|
837
|
+
});
|
|
838
|
+
currentStep = n;
|
|
839
|
+
|
|
840
|
+
// Trigger step-specific actions
|
|
841
|
+
if (n === 1) detectStack();
|
|
842
|
+
if (n === 2) loadConfigOptions();
|
|
843
|
+
if (n === 3) showPlugins();
|
|
844
|
+
if (n === 4) loadSecrets();
|
|
845
|
+
if (n === 5) runBoot();
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// ── Step 0: Welcome ──
|
|
849
|
+
document.getElementById('btn-next-0').addEventListener('click', () => {
|
|
850
|
+
const name = document.getElementById('factory-name').value.trim();
|
|
851
|
+
const path = document.getElementById('project-path').value.trim();
|
|
852
|
+
if (!name) { document.getElementById('factory-name').focus(); return; }
|
|
853
|
+
if (!path) { document.getElementById('project-path').focus(); return; }
|
|
854
|
+
goToStep(1);
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
// ── Step 1: Detect ──
|
|
858
|
+
async function detectStack() {
|
|
859
|
+
const path = document.getElementById('project-path').value.trim();
|
|
860
|
+
const loading = document.getElementById('detect-loading');
|
|
861
|
+
const results = document.getElementById('detect-results');
|
|
862
|
+
const errorEl = document.getElementById('detect-error');
|
|
863
|
+
const nextBtn = document.getElementById('btn-next-1');
|
|
864
|
+
|
|
865
|
+
loading.style.display = 'flex';
|
|
866
|
+
results.style.display = 'none';
|
|
867
|
+
errorEl.style.display = 'none';
|
|
868
|
+
nextBtn.disabled = true;
|
|
869
|
+
|
|
870
|
+
try {
|
|
871
|
+
stackInfo = await api('POST', '/api/detect-stack', { path });
|
|
872
|
+
|
|
873
|
+
// Populate grid
|
|
874
|
+
const grid = document.getElementById('detect-grid');
|
|
875
|
+
grid.innerHTML = [
|
|
876
|
+
{ label: 'Framework', value: stackInfo.framework },
|
|
877
|
+
{ label: 'Language', value: stackInfo.language },
|
|
878
|
+
{ label: 'Package Manager', value: stackInfo.package_manager || 'none' },
|
|
879
|
+
{ label: 'Dev Port', value: stackInfo.dev_port },
|
|
880
|
+
...(stackInfo.git_remote ? [{ label: 'Git Remote', value: stackInfo.git_remote }] : []),
|
|
881
|
+
{ label: 'Docker', value: stackInfo.has_docker ? 'Yes' : 'No' },
|
|
882
|
+
].map(item => `
|
|
883
|
+
<div class="detect-item">
|
|
884
|
+
<div class="detect-label">${item.label}</div>
|
|
885
|
+
<div class="detect-value">${item.value}</div>
|
|
886
|
+
</div>
|
|
887
|
+
`).join('');
|
|
888
|
+
|
|
889
|
+
// Populate commands
|
|
890
|
+
const cmdList = document.getElementById('command-list');
|
|
891
|
+
cmdList.innerHTML = ['build', 'dev', 'test', 'install'].map(cmd => `
|
|
892
|
+
<div class="command-row">
|
|
893
|
+
<span class="command-label">${cmd}</span>
|
|
894
|
+
<span class="command-value">${stackInfo.suggested_commands[cmd]}</span>
|
|
895
|
+
</div>
|
|
896
|
+
`).join('');
|
|
897
|
+
|
|
898
|
+
// Set override defaults
|
|
899
|
+
document.getElementById('override-build').value = stackInfo.suggested_commands.build;
|
|
900
|
+
document.getElementById('override-dev').value = stackInfo.suggested_commands.dev;
|
|
901
|
+
document.getElementById('override-test').value = stackInfo.suggested_commands.test;
|
|
902
|
+
document.getElementById('override-install').value = stackInfo.suggested_commands.install;
|
|
903
|
+
document.getElementById('override-port').value = stackInfo.dev_port;
|
|
904
|
+
|
|
905
|
+
selectedPreset = stackInfo.suggested_preset;
|
|
906
|
+
selectedDeploy = stackInfo.suggested_deploy;
|
|
907
|
+
|
|
908
|
+
loading.style.display = 'none';
|
|
909
|
+
results.style.display = 'block';
|
|
910
|
+
nextBtn.disabled = false;
|
|
911
|
+
} catch (err) {
|
|
912
|
+
loading.style.display = 'none';
|
|
913
|
+
errorEl.style.display = 'block';
|
|
914
|
+
errorEl.textContent = err.message;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
document.getElementById('toggle-overrides').addEventListener('click', () => {
|
|
919
|
+
const form = document.getElementById('override-form');
|
|
920
|
+
form.classList.toggle('visible');
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
document.getElementById('btn-prev-1').addEventListener('click', () => goToStep(0));
|
|
924
|
+
document.getElementById('btn-next-1').addEventListener('click', () => {
|
|
925
|
+
// Apply overrides if form is visible
|
|
926
|
+
const form = document.getElementById('override-form');
|
|
927
|
+
if (form.classList.contains('visible') && stackInfo) {
|
|
928
|
+
stackInfo.suggested_commands.build = document.getElementById('override-build').value;
|
|
929
|
+
stackInfo.suggested_commands.dev = document.getElementById('override-dev').value;
|
|
930
|
+
stackInfo.suggested_commands.test = document.getElementById('override-test').value;
|
|
931
|
+
stackInfo.suggested_commands.install = document.getElementById('override-install').value;
|
|
932
|
+
stackInfo.dev_port = parseInt(document.getElementById('override-port').value, 10) || stackInfo.dev_port;
|
|
933
|
+
}
|
|
934
|
+
goToStep(2);
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
// ── Step 2: Configure ──
|
|
938
|
+
async function loadConfigOptions() {
|
|
939
|
+
try {
|
|
940
|
+
// Load presets
|
|
941
|
+
if (presetsData.length === 0) {
|
|
942
|
+
presetsData = await api('GET', '/api/presets');
|
|
943
|
+
}
|
|
944
|
+
const presetCards = document.getElementById('preset-cards');
|
|
945
|
+
presetCards.innerHTML = presetsData.map(p => {
|
|
946
|
+
const isRecommended = stackInfo && p.name === stackInfo.suggested_preset;
|
|
947
|
+
const isSelected = p.name === selectedPreset;
|
|
948
|
+
return `
|
|
949
|
+
<label class="radio-card${isSelected ? ' selected' : ''}" data-preset="${p.name}">
|
|
950
|
+
<input type="radio" name="preset" value="${p.name}" ${isSelected ? 'checked' : ''}>
|
|
951
|
+
<div class="radio-indicator"></div>
|
|
952
|
+
<div class="radio-info">
|
|
953
|
+
<h4>${p.name}${isRecommended ? '<span class="badge">Recommended</span>' : ''}</h4>
|
|
954
|
+
<p>${p.description}</p>
|
|
955
|
+
<p>Threshold: ${p.satisfaction_threshold} | Max iterations: ${p.max_iterations}</p>
|
|
956
|
+
</div>
|
|
957
|
+
</label>
|
|
958
|
+
`;
|
|
959
|
+
}).join('');
|
|
960
|
+
|
|
961
|
+
// Preset card click handlers
|
|
962
|
+
presetCards.querySelectorAll('.radio-card').forEach(card => {
|
|
963
|
+
card.addEventListener('click', () => {
|
|
964
|
+
presetCards.querySelectorAll('.radio-card').forEach(c => c.classList.remove('selected'));
|
|
965
|
+
card.classList.add('selected');
|
|
966
|
+
card.querySelector('input').checked = true;
|
|
967
|
+
selectedPreset = card.dataset.preset;
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// Load backends
|
|
972
|
+
const backends = await api('GET', '/api/task-backends');
|
|
973
|
+
const backendSelect = document.getElementById('backend-select');
|
|
974
|
+
backendSelect.innerHTML = backends.map(b =>
|
|
975
|
+
`<option value="${b.value}" ${b.value === selectedBackend ? 'selected' : ''}>${b.label}</option>`
|
|
976
|
+
).join('');
|
|
977
|
+
backendSelect.addEventListener('change', () => { selectedBackend = backendSelect.value; });
|
|
978
|
+
|
|
979
|
+
// Load deploy targets
|
|
980
|
+
const targets = await api('GET', '/api/deploy-targets');
|
|
981
|
+
const deploySelect = document.getElementById('deploy-select');
|
|
982
|
+
deploySelect.innerHTML = targets.map(t => {
|
|
983
|
+
const isRecommended = stackInfo && t === stackInfo.suggested_deploy;
|
|
984
|
+
return `<option value="${t}" ${t === selectedDeploy ? 'selected' : ''}>${t}${isRecommended ? ' (recommended)' : ''}</option>`;
|
|
985
|
+
}).join('');
|
|
986
|
+
deploySelect.addEventListener('change', () => { selectedDeploy = deploySelect.value; });
|
|
987
|
+
} catch (err) {
|
|
988
|
+
console.error('Failed to load config options:', err);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
document.getElementById('btn-prev-2').addEventListener('click', () => goToStep(1));
|
|
993
|
+
document.getElementById('btn-next-2').addEventListener('click', () => goToStep(3));
|
|
994
|
+
|
|
995
|
+
// ── Step 3: Extend ──
|
|
996
|
+
function showPlugins() {
|
|
997
|
+
const plugins = stackInfo ? stackInfo.suggested_plugins : [];
|
|
998
|
+
const container = document.getElementById('plugin-cards');
|
|
999
|
+
const empty = document.getElementById('no-plugins');
|
|
1000
|
+
|
|
1001
|
+
const PLUGIN_DESCRIPTIONS = {
|
|
1002
|
+
nextjs: 'Next.js-specific optimizations and conventions',
|
|
1003
|
+
playwright: 'Browser-based scenario verification with Playwright',
|
|
1004
|
+
prisma: 'Prisma ORM integration for database operations',
|
|
1005
|
+
tailwind: 'Tailwind CSS utility-first styling',
|
|
1006
|
+
trpc: 'Type-safe API layer with tRPC',
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
if (plugins.length === 0) {
|
|
1010
|
+
container.innerHTML = '';
|
|
1011
|
+
empty.style.display = 'block';
|
|
1012
|
+
} else {
|
|
1013
|
+
empty.style.display = 'none';
|
|
1014
|
+
container.innerHTML = plugins.map(p => `
|
|
1015
|
+
<div class="plugin-card">
|
|
1016
|
+
<div>
|
|
1017
|
+
<div class="plugin-name">${p}</div>
|
|
1018
|
+
<div class="plugin-desc">${PLUGIN_DESCRIPTIONS[p] || 'Stack-specific plugin'}</div>
|
|
1019
|
+
</div>
|
|
1020
|
+
<span class="badge-soon">Coming Soon</span>
|
|
1021
|
+
</div>
|
|
1022
|
+
`).join('');
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
document.getElementById('btn-prev-3').addEventListener('click', () => goToStep(2));
|
|
1027
|
+
document.getElementById('btn-next-3').addEventListener('click', () => goToStep(4));
|
|
1028
|
+
|
|
1029
|
+
// ── Step 4: Connect ──
|
|
1030
|
+
async function loadSecrets() {
|
|
1031
|
+
const loading = document.getElementById('secrets-loading');
|
|
1032
|
+
const list = document.getElementById('secrets-list');
|
|
1033
|
+
|
|
1034
|
+
loading.style.display = 'flex';
|
|
1035
|
+
list.style.display = 'none';
|
|
1036
|
+
|
|
1037
|
+
try {
|
|
1038
|
+
const config = {
|
|
1039
|
+
pipeline: { preset: selectedPreset },
|
|
1040
|
+
task_backend: { adapter: selectedBackend },
|
|
1041
|
+
};
|
|
1042
|
+
secretReport = await api('POST', '/api/validate-secrets', {
|
|
1043
|
+
config,
|
|
1044
|
+
env: {},
|
|
1045
|
+
context: { deploy_target: selectedDeploy },
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
list.innerHTML = secretReport.required.map(req => {
|
|
1049
|
+
const isPresent = secretReport.present.includes(req.name);
|
|
1050
|
+
const isOptional = !req.required;
|
|
1051
|
+
return `
|
|
1052
|
+
<div class="secret-field">
|
|
1053
|
+
<div class="secret-status ${isPresent ? 'present' : isOptional ? 'present' : 'missing'}"></div>
|
|
1054
|
+
<div class="secret-info">
|
|
1055
|
+
<div class="secret-name">${req.name}${isOptional ? ' <span style="opacity:0.5;font-size:12px">(optional)</span>' : ''}</div>
|
|
1056
|
+
<div class="secret-reason">${req.reason}</div>
|
|
1057
|
+
</div>
|
|
1058
|
+
${isPresent
|
|
1059
|
+
? '<span class="found-label">Found in environment</span>'
|
|
1060
|
+
: isOptional
|
|
1061
|
+
? `<input type="password" placeholder="Optional — skip for CLI auth" data-secret="${req.name}" />`
|
|
1062
|
+
: `<input type="password" placeholder="Enter value or skip" data-secret="${req.name}" />`
|
|
1063
|
+
}
|
|
1064
|
+
</div>
|
|
1065
|
+
`;
|
|
1066
|
+
}).join('');
|
|
1067
|
+
|
|
1068
|
+
loading.style.display = 'none';
|
|
1069
|
+
list.style.display = 'block';
|
|
1070
|
+
} catch (err) {
|
|
1071
|
+
loading.style.display = 'none';
|
|
1072
|
+
list.style.display = 'block';
|
|
1073
|
+
list.innerHTML = `<div style="color:var(--warning);padding:12px 0;">Could not check secrets: ${err.message}. You can add them later.</div>`;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
document.getElementById('btn-prev-4').addEventListener('click', () => goToStep(3));
|
|
1078
|
+
document.getElementById('btn-next-4').addEventListener('click', () => goToStep(5));
|
|
1079
|
+
|
|
1080
|
+
// ── Step 5: Boot ──
|
|
1081
|
+
async function runBoot() {
|
|
1082
|
+
const factoryName = document.getElementById('factory-name').value.trim();
|
|
1083
|
+
const projectPath = document.getElementById('project-path').value.trim();
|
|
1084
|
+
|
|
1085
|
+
// Hide progress bar during boot
|
|
1086
|
+
document.getElementById('progress').style.display = 'none';
|
|
1087
|
+
document.querySelector('.wizard-header').style.display = 'none';
|
|
1088
|
+
|
|
1089
|
+
// Collect secrets from inputs
|
|
1090
|
+
const secrets = {};
|
|
1091
|
+
document.querySelectorAll('#secrets-list input[type="password"]').forEach(input => {
|
|
1092
|
+
const val = input.value.trim();
|
|
1093
|
+
if (val) secrets[input.dataset.secret] = val;
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
// Prepare scaffold payload
|
|
1097
|
+
const config = {
|
|
1098
|
+
pipeline: { preset: selectedPreset },
|
|
1099
|
+
task_backend: { adapter: selectedBackend },
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
try {
|
|
1103
|
+
// Call scaffold API
|
|
1104
|
+
const result = await api('POST', '/api/scaffold', {
|
|
1105
|
+
name: factoryName,
|
|
1106
|
+
config,
|
|
1107
|
+
project: {
|
|
1108
|
+
name: projectPath.split('/').pop() || factoryName,
|
|
1109
|
+
path: projectPath,
|
|
1110
|
+
stack: stackInfo,
|
|
1111
|
+
deploy_target: selectedDeploy,
|
|
1112
|
+
},
|
|
1113
|
+
secrets,
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
// Run boot animation with real data
|
|
1117
|
+
await playBootAnimation(factoryName, result.files);
|
|
1118
|
+
} catch (err) {
|
|
1119
|
+
// Show error in boot screen
|
|
1120
|
+
const screen = document.getElementById('boot-screen');
|
|
1121
|
+
screen.innerHTML = `
|
|
1122
|
+
<div style="color:var(--danger);font-size:16px;padding:20px;">
|
|
1123
|
+
<p style="font-weight:600;margin-bottom:8px;">Scaffold failed</p>
|
|
1124
|
+
<p style="font-family:var(--font-mono);font-size:13px;">${err.message}</p>
|
|
1125
|
+
<button class="btn" style="margin-top:20px;" onclick="goToStep(4)">Go Back</button>
|
|
1126
|
+
</div>
|
|
1127
|
+
`;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Make goToStep available globally for error recovery
|
|
1132
|
+
window.goToStep = goToStep;
|
|
1133
|
+
|
|
1134
|
+
async function playBootAnimation(factoryName, files) {
|
|
1135
|
+
const delay = (ms) => new Promise(r => setTimeout(r, ms));
|
|
1136
|
+
|
|
1137
|
+
// Phase 1: Blinking cursor (already visible)
|
|
1138
|
+
await delay(400);
|
|
1139
|
+
|
|
1140
|
+
// Phase 2: Factory name types in
|
|
1141
|
+
const cursorLine = document.getElementById('boot-cursor-line');
|
|
1142
|
+
cursorLine.style.display = 'none';
|
|
1143
|
+
const titleContainer = document.getElementById('boot-title-container');
|
|
1144
|
+
titleContainer.style.display = 'block';
|
|
1145
|
+
const titleEl = document.getElementById('boot-title');
|
|
1146
|
+
titleEl.textContent = '> ' + factoryName;
|
|
1147
|
+
titleEl.style.opacity = '1';
|
|
1148
|
+
titleEl.classList.add('typing');
|
|
1149
|
+
await delay(1000);
|
|
1150
|
+
titleEl.style.borderRight = 'none';
|
|
1151
|
+
|
|
1152
|
+
// Phase 3: Config sections materialize
|
|
1153
|
+
const configContainer = document.getElementById('boot-config');
|
|
1154
|
+
configContainer.style.display = 'block';
|
|
1155
|
+
const configSections = [
|
|
1156
|
+
{ label: 'Pipeline', value: selectedPreset },
|
|
1157
|
+
{ label: 'Backend', value: selectedBackend },
|
|
1158
|
+
{ label: 'Deploy', value: selectedDeploy },
|
|
1159
|
+
{ label: 'Framework', value: stackInfo ? stackInfo.framework : 'unknown' },
|
|
1160
|
+
];
|
|
1161
|
+
for (const section of configSections) {
|
|
1162
|
+
const div = document.createElement('div');
|
|
1163
|
+
div.className = 'boot-config-section';
|
|
1164
|
+
div.innerHTML = `
|
|
1165
|
+
<span class="section-label">${section.label}</span>
|
|
1166
|
+
<span class="section-value">${section.value}</span>
|
|
1167
|
+
`;
|
|
1168
|
+
configContainer.appendChild(div);
|
|
1169
|
+
await delay(150);
|
|
1170
|
+
div.classList.add('visible');
|
|
1171
|
+
}
|
|
1172
|
+
await delay(300);
|
|
1173
|
+
|
|
1174
|
+
// Phase 4: File tree scaffolds
|
|
1175
|
+
const filesContainer = document.getElementById('boot-files');
|
|
1176
|
+
filesContainer.style.display = 'block';
|
|
1177
|
+
const headerLine = document.createElement('div');
|
|
1178
|
+
headerLine.className = 'boot-line';
|
|
1179
|
+
headerLine.innerHTML = '<span style="color:var(--accent);">Scaffolding files...</span>';
|
|
1180
|
+
filesContainer.appendChild(headerLine);
|
|
1181
|
+
await delay(100);
|
|
1182
|
+
headerLine.classList.add('visible');
|
|
1183
|
+
|
|
1184
|
+
for (const file of files) {
|
|
1185
|
+
const line = document.createElement('div');
|
|
1186
|
+
line.className = 'boot-line file-line';
|
|
1187
|
+
line.innerHTML = `<span class="checkmark">\u2713</span>${file}`;
|
|
1188
|
+
filesContainer.appendChild(line);
|
|
1189
|
+
await delay(80);
|
|
1190
|
+
line.classList.add('visible');
|
|
1191
|
+
await delay(60);
|
|
1192
|
+
line.querySelector('.checkmark').classList.add('visible');
|
|
1193
|
+
}
|
|
1194
|
+
await delay(400);
|
|
1195
|
+
|
|
1196
|
+
// Phase 5: Connection status dots
|
|
1197
|
+
const connContainer = document.getElementById('boot-connections');
|
|
1198
|
+
connContainer.style.display = 'block';
|
|
1199
|
+
const connHeader = document.createElement('div');
|
|
1200
|
+
connHeader.className = 'boot-line';
|
|
1201
|
+
connHeader.innerHTML = '<span style="color:var(--accent);">Checking connections...</span>';
|
|
1202
|
+
connContainer.appendChild(connHeader);
|
|
1203
|
+
await delay(100);
|
|
1204
|
+
connHeader.classList.add('visible');
|
|
1205
|
+
|
|
1206
|
+
if (secretReport && secretReport.required) {
|
|
1207
|
+
for (const req of secretReport.required) {
|
|
1208
|
+
const isPresent = secretReport.present.includes(req.name);
|
|
1209
|
+
const line = document.createElement('div');
|
|
1210
|
+
line.className = 'boot-line';
|
|
1211
|
+
line.innerHTML = `<span class="boot-status-dot ${isPresent ? 'green' : 'amber'}"></span>${req.name}`;
|
|
1212
|
+
connContainer.appendChild(line);
|
|
1213
|
+
await delay(100);
|
|
1214
|
+
line.classList.add('visible');
|
|
1215
|
+
await delay(100);
|
|
1216
|
+
line.querySelector('.boot-status-dot').classList.add('visible');
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
await delay(600);
|
|
1220
|
+
|
|
1221
|
+
// Phase 6: Factory ready!
|
|
1222
|
+
const factoryNameSlots = document.querySelectorAll('.factory-name-slot');
|
|
1223
|
+
factoryNameSlots.forEach(el => { el.textContent = factoryName; });
|
|
1224
|
+
const readyEl = document.getElementById('boot-ready');
|
|
1225
|
+
readyEl.classList.add('visible');
|
|
1226
|
+
|
|
1227
|
+
// Copy-to-clipboard on code blocks
|
|
1228
|
+
readyEl.querySelectorAll('code').forEach(code => {
|
|
1229
|
+
code.addEventListener('click', () => {
|
|
1230
|
+
navigator.clipboard.writeText(code.textContent).then(() => {
|
|
1231
|
+
const orig = code.style.borderColor;
|
|
1232
|
+
code.style.borderColor = 'var(--accent)';
|
|
1233
|
+
setTimeout(() => { code.style.borderColor = orig; }, 1000);
|
|
1234
|
+
});
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
})();
|
|
1239
|
+
</script>
|
|
1240
|
+
</body>
|
|
1241
|
+
</html>
|