@monster-orange/den 0.2.8 → 0.3.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/CHANGELOG.md +1547 -0
- package/README.md +4 -2
- package/bin/den +2 -1
- package/docs/distributions.md +507 -4
- package/make/den.mk +19 -4
- package/make/journeys.mk +2 -1
- package/make/local_dev.mk +6 -13
- package/make/secrets-1password.mk +18 -0
- package/make/test.mk +1 -1
- package/package.json +9 -2
- package/schemas/den.plugin.v1.json +156 -0
- package/schemas/den.theme.v1.json +269 -0
- package/scripts/auth-configure.ts +16 -8
- package/scripts/heal-config.ts +0 -5
- package/scripts/setup-1password.ts +41 -19
- package/scripts/setup-secrets.ts +67 -101
- package/src/pillars/api/global-setup.ts +1 -1
- package/src/pillars/journeys/sandbox.ts +23 -18
- package/src/pillars/journeys/tools/browser-session.ts +2 -2
- package/src/scripts/setup-default-workspace.ts +2 -2
- package/src/shared/auth/headless-login.ts +1 -1
- package/src/shared/auth/sudo-vault.ts +93 -31
- package/src/shared/auth/token-providers/index.ts +7 -3
- package/src/shared/brand.ts +7 -0
- package/src/shared/config-error.ts +33 -3
- package/src/shared/constants.ts +3 -24
- package/src/shared/den-config.ts +141 -5
- package/src/shared/edge/config.ts +147 -21
- package/src/shared/edge/exec.ts +31 -0
- package/src/shared/edge/included.ts +169 -0
- package/src/shared/edge/render.ts +69 -14
- package/src/shared/edge/runtime.ts +128 -121
- package/src/shared/edge/status.ts +68 -8
- package/src/shared/edge/tls.ts +328 -0
- package/src/shared/edge/types.ts +25 -9
- package/src/shared/environment-manager/default-workspace.ts +16 -1
- package/src/shared/environment-manager/health.ts +73 -38
- package/src/shared/environment-manager/scope.ts +168 -0
- package/src/shared/environment-manager/service-runner.ts +789 -43
- package/src/shared/environment-manager/up-targets.ts +132 -0
- package/src/shared/health/rollup.ts +48 -0
- package/src/shared/open-url.ts +21 -2
- package/src/shared/org-sources.ts +78 -59
- package/src/shared/org.ts +53 -5
- package/src/shared/plugins/manifest.ts +406 -0
- package/src/shared/plugins/store.ts +358 -0
- package/src/shared/process/legacy-adopt.ts +285 -0
- package/src/shared/process/process-group.ts +202 -0
- package/src/shared/process/spawn-streaming.ts +7 -0
- package/src/shared/secrets/doppler.ts +1 -1
- package/src/shared/secrets/index.ts +279 -50
- package/src/shared/secrets/none.ts +56 -0
- package/src/shared/secrets/strategies.ts +202 -0
- package/src/shared/secrets/types.ts +1 -1
- package/src/shared/smoke/git-remote.ts +40 -0
- package/src/shared/stacks/manifest-commands.ts +1 -1
- package/src/shared/stacks/namespace.ts +178 -0
- package/src/shared/stacks/owners.ts +303 -0
- package/src/shared/stacks/render.ts +8 -3
- package/src/shared/stacks/runtime.ts +100 -16
- package/src/shared/stacks/status.ts +63 -12
- package/src/shared/strategies/registry.ts +89 -0
- package/src/shared/theme/contract.ts +538 -0
- package/src/shared/types/index.ts +12 -3
- package/src/ui/cli/create.ts +24 -7
- package/src/ui/cli/doctor-command.ts +131 -0
- package/src/ui/cli/edge-command.ts +56 -10
- package/src/ui/cli/flag-parser.ts +7 -2
- package/src/ui/cli/health-display.ts +25 -0
- package/src/ui/cli/index.ts +548 -60
- package/src/ui/cli/notice.ts +20 -0
- package/src/ui/cli/plugin-command.ts +849 -0
- package/src/ui/cli/stack-command.ts +63 -2
- package/src/ui/cli/test-runner.ts +11 -9
- package/src/ui/cli/theme-command.ts +136 -0
- package/src/ui/cli/web-command.ts +47 -23
- package/src/ui/mcp/api.ts +28 -2
- package/src/ui/mcp/scope.ts +22 -1
- package/src/ui/mcp/tools.ts +50 -7
- package/src/ui/server/appearance-prefs.ts +95 -0
- package/src/ui/server/daemon-reaper.ts +29 -1
- package/src/ui/server/engine-stamp.ts +83 -0
- package/src/ui/server/github-service.ts +23 -44
- package/src/ui/server/graph-page.ts +20 -0
- package/src/ui/server/graph-prefs.ts +44 -1
- package/src/ui/server/http-errors.ts +293 -0
- package/src/ui/server/http-helpers.ts +22 -3
- package/src/ui/server/index.ts +532 -51
- package/src/ui/server/public/appearance.js +531 -0
- package/src/ui/server/public/edge-page.js +248 -0
- package/src/ui/server/public/edge.html +251 -0
- package/src/ui/server/public/environments.html +65 -83
- package/src/ui/server/public/error-page.js +247 -0
- package/src/ui/server/public/error.html +260 -0
- package/src/ui/server/public/graph-model.js +332 -14
- package/src/ui/server/public/graph.css +141 -47
- package/src/ui/server/public/graph.js +223 -44
- package/src/ui/server/public/header.js +396 -600
- package/src/ui/server/public/health-fit.js +248 -0
- package/src/ui/server/public/health-rollup.js +128 -0
- package/src/ui/server/public/i18n/es.js +287 -0
- package/src/ui/server/public/i18n/ja.js +276 -0
- package/src/ui/server/public/index.html +139 -438
- package/src/ui/server/public/local-dev.html +3468 -4044
- package/src/ui/server/public/logs.html +71 -208
- package/src/ui/server/public/panel.js +14 -2
- package/src/ui/server/public/release.html +175 -389
- package/src/ui/server/public/services.html +596 -770
- package/src/ui/server/public/settings-page.js +759 -0
- package/src/ui/server/public/settings-registry.js +521 -0
- package/src/ui/server/public/settings.html +590 -0
- package/src/ui/server/public/shell.js +3869 -0
- package/src/ui/server/public/topbar.js +1413 -0
- package/src/ui/server/public/version-label.js +12 -0
- package/src/ui/server/public/workspace-picker.js +74 -34
- package/src/ui/server/readonly-routes.ts +12 -1
- package/src/ui/server/services-list.ts +94 -17
- package/src/ui/server/settings-distribution.ts +74 -0
- package/src/ui/server/shell-prefs.ts +192 -0
- package/src/ui/server/theme-routes.ts +292 -0
- package/src/ui/server/theme-runtime.ts +407 -0
- package/src/ui/server/ui-assets.ts +16 -1
- package/src/ui/server/ui-language.ts +50 -0
- package/src/ui/server/workspace-routes.ts +45 -3
- package/themes/den/den-theme.json +72 -0
- package/themes/den/favicon.ico +0 -0
- package/themes/den/mark.svg +6 -0
|
@@ -3,8 +3,30 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<
|
|
7
|
-
|
|
6
|
+
<script>
|
|
7
|
+
// Mode before first paint: yours (the server fills it in below),
|
|
8
|
+
// else the system's, else the theme's default, else dark.
|
|
9
|
+
(function () {
|
|
10
|
+
var m = "@@DEN_MODE@@";
|
|
11
|
+
if (m !== "dark" && m !== "light") {
|
|
12
|
+
var q = function (s) {
|
|
13
|
+
return (
|
|
14
|
+
window.matchMedia &&
|
|
15
|
+
matchMedia("(prefers-color-scheme: " + s + ")").matches
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
m = q("light")
|
|
19
|
+
? "light"
|
|
20
|
+
: q("dark")
|
|
21
|
+
? "dark"
|
|
22
|
+
: "@@DEN_DEFAULT_MODE@@";
|
|
23
|
+
}
|
|
24
|
+
document.documentElement.dataset.mode =
|
|
25
|
+
m === "light" ? "light" : "dark";
|
|
26
|
+
})();
|
|
27
|
+
</script>
|
|
28
|
+
<link rel="stylesheet" href="/theme.css" id="theme-css" />
|
|
29
|
+
<title>__DEN_TITLE__ · Tests</title>
|
|
8
30
|
<style>
|
|
9
31
|
*,
|
|
10
32
|
*::before,
|
|
@@ -14,25 +36,12 @@
|
|
|
14
36
|
padding: 0;
|
|
15
37
|
}
|
|
16
38
|
|
|
17
|
-
:root {
|
|
18
|
-
--bg: #0d1117;
|
|
19
|
-
--surface: #161b22;
|
|
20
|
-
--border: #30363d;
|
|
21
|
-
--text: #c9d1d9;
|
|
22
|
-
--muted: #8b949e;
|
|
23
|
-
--accent: #58a6ff;
|
|
24
|
-
--green: #3fb950;
|
|
25
|
-
--red: #f85149;
|
|
26
|
-
--yellow: #d29922;
|
|
27
|
-
--mono: "SF Mono", "Cascadia Code", "Fira Code", "Menlo", monospace;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
39
|
html,
|
|
31
40
|
body {
|
|
32
41
|
height: 100%;
|
|
33
42
|
background: var(--bg);
|
|
34
43
|
color: var(--text);
|
|
35
|
-
font-family:
|
|
44
|
+
font-family: var(--sans);
|
|
36
45
|
font-size: 13px;
|
|
37
46
|
}
|
|
38
47
|
body {
|
|
@@ -42,75 +51,10 @@
|
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
/* ── Bar 1: top navbar ─────────────────────────────────────────────────── */
|
|
45
|
-
.topbar {
|
|
46
|
-
display: flex;
|
|
47
|
-
align-items: center;
|
|
48
|
-
gap: 10px;
|
|
49
|
-
padding: 7px 14px;
|
|
50
|
-
background: var(--surface);
|
|
51
|
-
border-bottom: 1px solid var(--border);
|
|
52
|
-
flex-shrink: 0;
|
|
53
|
-
z-index: 10;
|
|
54
|
-
}
|
|
55
|
-
.topbar h1 {
|
|
56
|
-
font-family: var(--mono);
|
|
57
|
-
font-size: 14px;
|
|
58
|
-
font-weight: 600;
|
|
59
|
-
letter-spacing: -0.02em;
|
|
60
|
-
margin-right: 4px;
|
|
61
|
-
}
|
|
62
54
|
|
|
63
55
|
/* ── Env badge (subbar selector pill) ────────────────────────────────── */
|
|
64
|
-
.env-badge {
|
|
65
|
-
display: inline-flex;
|
|
66
|
-
align-items: center;
|
|
67
|
-
gap: 5px;
|
|
68
|
-
padding: 2px 8px 2px 6px;
|
|
69
|
-
border-radius: 999px;
|
|
70
|
-
background: var(--surface);
|
|
71
|
-
border: 1px solid var(--border);
|
|
72
|
-
font-size: 11px;
|
|
73
|
-
font-weight: 500;
|
|
74
|
-
color: var(--muted);
|
|
75
|
-
}
|
|
76
|
-
.env-badge::before {
|
|
77
|
-
content: "env";
|
|
78
|
-
font-size: 9px;
|
|
79
|
-
font-weight: 600;
|
|
80
|
-
letter-spacing: 0.05em;
|
|
81
|
-
color: var(--muted);
|
|
82
|
-
opacity: 0.6;
|
|
83
|
-
text-transform: uppercase;
|
|
84
|
-
}
|
|
85
|
-
.env-badge select {
|
|
86
|
-
background: transparent;
|
|
87
|
-
border: none;
|
|
88
|
-
outline: none;
|
|
89
|
-
color: var(--text);
|
|
90
|
-
font-size: 11px;
|
|
91
|
-
font-weight: 600;
|
|
92
|
-
cursor: pointer;
|
|
93
|
-
padding: 0;
|
|
94
|
-
-webkit-appearance: none;
|
|
95
|
-
appearance: none;
|
|
96
|
-
}
|
|
97
|
-
.env-badge select option {
|
|
98
|
-
background: var(--surface);
|
|
99
|
-
}
|
|
100
56
|
|
|
101
57
|
/* ── Topbar env pill ──────────────────────────────────────────────────── */
|
|
102
|
-
#topbar-env-badge {
|
|
103
|
-
display: none;
|
|
104
|
-
align-items: center;
|
|
105
|
-
padding: 2px 7px;
|
|
106
|
-
border-radius: 999px;
|
|
107
|
-
border: 1px solid var(--border);
|
|
108
|
-
font-size: 10px;
|
|
109
|
-
font-weight: 600;
|
|
110
|
-
letter-spacing: 0.03em;
|
|
111
|
-
color: var(--muted);
|
|
112
|
-
white-space: nowrap;
|
|
113
|
-
}
|
|
114
58
|
|
|
115
59
|
/* ── Bar 2: sub-navbar ─────────────────────────────────────────────────── */
|
|
116
60
|
.subbar {
|
|
@@ -137,20 +81,20 @@
|
|
|
137
81
|
display: inline-block;
|
|
138
82
|
}
|
|
139
83
|
.dot.ok {
|
|
140
|
-
background: var(--
|
|
84
|
+
background: var(--ok);
|
|
141
85
|
}
|
|
142
86
|
.dot.err {
|
|
143
|
-
background: var(--
|
|
87
|
+
background: var(--bad);
|
|
144
88
|
}
|
|
145
89
|
.dot.running {
|
|
146
|
-
background: var(--
|
|
90
|
+
background: var(--warn);
|
|
147
91
|
animation: pulse 1s ease-in-out infinite;
|
|
148
92
|
}
|
|
149
93
|
.dot.passed {
|
|
150
|
-
background: var(--
|
|
94
|
+
background: var(--ok);
|
|
151
95
|
}
|
|
152
96
|
.dot.failed {
|
|
153
|
-
background: var(--
|
|
97
|
+
background: var(--bad);
|
|
154
98
|
}
|
|
155
99
|
@keyframes pulse {
|
|
156
100
|
0%,
|
|
@@ -190,33 +134,9 @@
|
|
|
190
134
|
color: var(--text);
|
|
191
135
|
}
|
|
192
136
|
|
|
193
|
-
nav {
|
|
194
|
-
display: flex;
|
|
195
|
-
gap: 2px;
|
|
196
|
-
}
|
|
197
|
-
nav a {
|
|
198
|
-
color: var(--muted);
|
|
199
|
-
text-decoration: none;
|
|
200
|
-
padding: 3px 10px;
|
|
201
|
-
border-radius: 4px;
|
|
202
|
-
font-size: 12px;
|
|
203
|
-
font-weight: 500;
|
|
204
|
-
transition: color 0.1s;
|
|
205
|
-
display: flex;
|
|
206
|
-
align-items: center;
|
|
207
|
-
gap: 5px;
|
|
208
|
-
}
|
|
209
|
-
nav a:hover {
|
|
210
|
-
color: var(--text);
|
|
211
|
-
background: var(--border);
|
|
212
|
-
}
|
|
213
|
-
nav a.active {
|
|
214
|
-
color: var(--text);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
137
|
.badge {
|
|
218
|
-
background: var(--
|
|
219
|
-
color:
|
|
138
|
+
background: var(--warn);
|
|
139
|
+
color: var(--bg);
|
|
220
140
|
font-size: 10px;
|
|
221
141
|
font-weight: 700;
|
|
222
142
|
padding: 1px 5px;
|
|
@@ -275,7 +195,7 @@
|
|
|
275
195
|
border-bottom: 1px solid var(--border);
|
|
276
196
|
}
|
|
277
197
|
.sec-table td a {
|
|
278
|
-
color: var(--accent
|
|
198
|
+
color: var(--accent-text);
|
|
279
199
|
}
|
|
280
200
|
.sec-finding td {
|
|
281
201
|
color: var(--muted);
|
|
@@ -284,20 +204,20 @@
|
|
|
284
204
|
}
|
|
285
205
|
.sec-hit.sec-high,
|
|
286
206
|
span.sec-high {
|
|
287
|
-
color:
|
|
207
|
+
color: var(--bad);
|
|
288
208
|
font-weight: 600;
|
|
289
209
|
}
|
|
290
210
|
.sec-hit.sec-medium,
|
|
291
211
|
span.sec-medium {
|
|
292
|
-
color:
|
|
212
|
+
color: var(--warn);
|
|
293
213
|
font-weight: 600;
|
|
294
214
|
}
|
|
295
215
|
.sec-hit.sec-low,
|
|
296
216
|
span.sec-low {
|
|
297
|
-
color:
|
|
217
|
+
color: var(--muted);
|
|
298
218
|
}
|
|
299
219
|
.sec-err {
|
|
300
|
-
color:
|
|
220
|
+
color: var(--bad);
|
|
301
221
|
}
|
|
302
222
|
.smoke-override {
|
|
303
223
|
display: none;
|
|
@@ -380,7 +300,7 @@
|
|
|
380
300
|
min-width: 180px;
|
|
381
301
|
max-height: 260px;
|
|
382
302
|
overflow-y: auto;
|
|
383
|
-
box-shadow:
|
|
303
|
+
box-shadow: var(--shadow);
|
|
384
304
|
}
|
|
385
305
|
.module-picker-dropdown.open {
|
|
386
306
|
display: block;
|
|
@@ -420,40 +340,11 @@
|
|
|
420
340
|
}
|
|
421
341
|
|
|
422
342
|
/* ── Health indicators ─────────────────────────────────────────────────── */
|
|
423
|
-
.health {
|
|
424
|
-
display: flex;
|
|
425
|
-
align-items: center;
|
|
426
|
-
gap: 10px;
|
|
427
|
-
padding: 0 4px;
|
|
428
|
-
}
|
|
429
|
-
.health-item {
|
|
430
|
-
display: flex;
|
|
431
|
-
align-items: center;
|
|
432
|
-
gap: 5px;
|
|
433
|
-
font-size: 11px;
|
|
434
|
-
color: var(--muted);
|
|
435
|
-
}
|
|
436
|
-
.health-group {
|
|
437
|
-
display: flex;
|
|
438
|
-
align-items: center;
|
|
439
|
-
gap: 4px;
|
|
440
|
-
font-size: 11px;
|
|
441
|
-
color: var(--muted);
|
|
442
|
-
}
|
|
443
|
-
.health-group-label {
|
|
444
|
-
color: var(--muted);
|
|
445
|
-
margin-right: 1px;
|
|
446
|
-
}
|
|
447
|
-
.health-group-dots {
|
|
448
|
-
display: flex;
|
|
449
|
-
align-items: center;
|
|
450
|
-
gap: 3px;
|
|
451
|
-
}
|
|
452
343
|
|
|
453
344
|
/* ── Buttons ───────────────────────────────────────────────────────────── */
|
|
454
345
|
.run-btn {
|
|
455
|
-
background: var(--accent);
|
|
456
|
-
color:
|
|
346
|
+
background: var(--accent-fill);
|
|
347
|
+
color: var(--on-accent);
|
|
457
348
|
border: none;
|
|
458
349
|
padding: 5px 18px;
|
|
459
350
|
border-radius: 6px;
|
|
@@ -584,7 +475,7 @@
|
|
|
584
475
|
font-weight: 600;
|
|
585
476
|
}
|
|
586
477
|
.test-source-ws {
|
|
587
|
-
color: var(--accent);
|
|
478
|
+
color: var(--accent-text);
|
|
588
479
|
}
|
|
589
480
|
.test-source-branch {
|
|
590
481
|
color: var(--text);
|
|
@@ -626,8 +517,10 @@
|
|
|
626
517
|
.tests-main.layout-top {
|
|
627
518
|
flex-direction: column;
|
|
628
519
|
}
|
|
520
|
+
/* The output is the shell's runs dock (R16): the controls take their
|
|
521
|
+
own height. */
|
|
629
522
|
.tests-main.layout-top .left-panel {
|
|
630
|
-
flex: 0 0
|
|
523
|
+
flex: 0 0 auto;
|
|
631
524
|
border-bottom: 1px solid var(--border);
|
|
632
525
|
}
|
|
633
526
|
.tests-main.layout-top .report-pane {
|
|
@@ -652,36 +545,6 @@
|
|
|
652
545
|
flex-wrap: wrap;
|
|
653
546
|
}
|
|
654
547
|
|
|
655
|
-
.output-pane {
|
|
656
|
-
flex: 1;
|
|
657
|
-
display: flex;
|
|
658
|
-
flex-direction: column;
|
|
659
|
-
min-height: 0;
|
|
660
|
-
}
|
|
661
|
-
#test-output {
|
|
662
|
-
flex: 1;
|
|
663
|
-
overflow-y: auto;
|
|
664
|
-
padding: 10px 14px;
|
|
665
|
-
font-family: var(--mono);
|
|
666
|
-
font-size: 11.5px;
|
|
667
|
-
line-height: 1.65;
|
|
668
|
-
white-space: pre-wrap;
|
|
669
|
-
word-break: break-all;
|
|
670
|
-
min-height: 0;
|
|
671
|
-
}
|
|
672
|
-
#test-output .cmd {
|
|
673
|
-
color: var(--accent);
|
|
674
|
-
}
|
|
675
|
-
#test-output .err {
|
|
676
|
-
color: var(--red);
|
|
677
|
-
}
|
|
678
|
-
#test-output .done-ok {
|
|
679
|
-
color: var(--green);
|
|
680
|
-
}
|
|
681
|
-
#test-output .done-fail {
|
|
682
|
-
color: var(--red);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
548
|
.divider {
|
|
686
549
|
flex-shrink: 0;
|
|
687
550
|
background: var(--border);
|
|
@@ -718,7 +581,7 @@
|
|
|
718
581
|
flex: 1;
|
|
719
582
|
border: none;
|
|
720
583
|
width: 100%;
|
|
721
|
-
background:
|
|
584
|
+
background: var(--bg);
|
|
722
585
|
display: none;
|
|
723
586
|
}
|
|
724
587
|
#report-placeholder {
|
|
@@ -804,8 +667,8 @@
|
|
|
804
667
|
width: 22px;
|
|
805
668
|
height: 22px;
|
|
806
669
|
border-radius: 6px;
|
|
807
|
-
background:
|
|
808
|
-
border: 1px solid
|
|
670
|
+
background: color-mix(in srgb, var(--bg) 75%, transparent);
|
|
671
|
+
border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
|
|
809
672
|
color: var(--muted);
|
|
810
673
|
font-size: 15px;
|
|
811
674
|
line-height: 1;
|
|
@@ -834,16 +697,16 @@
|
|
|
834
697
|
padding: 4px 8px;
|
|
835
698
|
}
|
|
836
699
|
.perf-ok {
|
|
837
|
-
background:
|
|
838
|
-
color: var(--
|
|
700
|
+
background: color-mix(in srgb, var(--ok) 15%, transparent);
|
|
701
|
+
color: var(--ok);
|
|
839
702
|
}
|
|
840
703
|
.perf-fail {
|
|
841
|
-
background:
|
|
842
|
-
color: var(--
|
|
704
|
+
background: color-mix(in srgb, var(--bad) 15%, transparent);
|
|
705
|
+
color: var(--bad);
|
|
843
706
|
}
|
|
844
707
|
.perf-section {
|
|
845
708
|
padding: 12px 14px;
|
|
846
|
-
border-top: 1px solid
|
|
709
|
+
border-top: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
|
|
847
710
|
}
|
|
848
711
|
.perf-section:first-of-type {
|
|
849
712
|
border-top: none;
|
|
@@ -855,10 +718,10 @@
|
|
|
855
718
|
margin-top: 10px;
|
|
856
719
|
}
|
|
857
720
|
.perf-stat {
|
|
858
|
-
border: 1px solid
|
|
721
|
+
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
|
859
722
|
border-radius: 8px;
|
|
860
723
|
padding: 10px;
|
|
861
|
-
background:
|
|
724
|
+
background: color-mix(in srgb, var(--bg) 55%, transparent);
|
|
862
725
|
display: flex;
|
|
863
726
|
flex-direction: column;
|
|
864
727
|
gap: 6px;
|
|
@@ -875,7 +738,7 @@
|
|
|
875
738
|
}
|
|
876
739
|
.perf-table th,
|
|
877
740
|
.perf-table td {
|
|
878
|
-
border-top: 1px solid
|
|
741
|
+
border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
|
879
742
|
padding: 8px 6px;
|
|
880
743
|
text-align: left;
|
|
881
744
|
vertical-align: top;
|
|
@@ -902,8 +765,8 @@
|
|
|
902
765
|
margin: 0 8px 6px 0;
|
|
903
766
|
padding: 3px 8px;
|
|
904
767
|
border-radius: 999px;
|
|
905
|
-
border: 1px solid
|
|
906
|
-
background:
|
|
768
|
+
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
|
769
|
+
background: color-mix(in srgb, var(--bg) 55%, transparent);
|
|
907
770
|
font-size: 11px;
|
|
908
771
|
white-space: nowrap;
|
|
909
772
|
}
|
|
@@ -968,7 +831,7 @@
|
|
|
968
831
|
}
|
|
969
832
|
.cmd-btn:hover:not(:disabled) {
|
|
970
833
|
border-color: var(--accent);
|
|
971
|
-
color: var(--accent);
|
|
834
|
+
color: var(--accent-text);
|
|
972
835
|
}
|
|
973
836
|
.cmd-btn:disabled {
|
|
974
837
|
opacity: 0.45;
|
|
@@ -978,12 +841,12 @@
|
|
|
978
841
|
border-color: var(--muted);
|
|
979
842
|
}
|
|
980
843
|
.logs-btn:hover:not(:disabled) {
|
|
981
|
-
border-color: var(--
|
|
982
|
-
color: var(--
|
|
844
|
+
border-color: var(--warn) !important;
|
|
845
|
+
color: var(--warn) !important;
|
|
983
846
|
}
|
|
984
847
|
.logs-btn.streaming {
|
|
985
|
-
border-color: var(--
|
|
986
|
-
color: var(--
|
|
848
|
+
border-color: var(--warn);
|
|
849
|
+
color: var(--warn);
|
|
987
850
|
}
|
|
988
851
|
|
|
989
852
|
.empty-state {
|
|
@@ -993,40 +856,6 @@
|
|
|
993
856
|
text-align: center;
|
|
994
857
|
}
|
|
995
858
|
|
|
996
|
-
/* exec output strip */
|
|
997
|
-
.cmd-strip {
|
|
998
|
-
flex: 1;
|
|
999
|
-
display: flex;
|
|
1000
|
-
flex-direction: column;
|
|
1001
|
-
min-height: 0;
|
|
1002
|
-
border-top: 1px solid var(--border);
|
|
1003
|
-
}
|
|
1004
|
-
.cmd-strip.hidden {
|
|
1005
|
-
display: none;
|
|
1006
|
-
}
|
|
1007
|
-
#cmd-output {
|
|
1008
|
-
flex: 1;
|
|
1009
|
-
overflow-y: auto;
|
|
1010
|
-
padding: 6px 14px;
|
|
1011
|
-
font-family: var(--mono);
|
|
1012
|
-
font-size: 11px;
|
|
1013
|
-
line-height: 1.55;
|
|
1014
|
-
white-space: pre-wrap;
|
|
1015
|
-
word-break: break-all;
|
|
1016
|
-
}
|
|
1017
|
-
#cmd-output .cmd {
|
|
1018
|
-
color: var(--accent);
|
|
1019
|
-
}
|
|
1020
|
-
#cmd-output .err {
|
|
1021
|
-
color: var(--red);
|
|
1022
|
-
}
|
|
1023
|
-
#cmd-output .done-ok {
|
|
1024
|
-
color: var(--green);
|
|
1025
|
-
}
|
|
1026
|
-
#cmd-output .done-fail {
|
|
1027
|
-
color: var(--red);
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
859
|
/* ── LOGS VIEW ─────────────────────────────────────────────────────────── */
|
|
1031
860
|
#view-logs {
|
|
1032
861
|
flex-direction: column;
|
|
@@ -1085,10 +914,10 @@
|
|
|
1085
914
|
min-height: 0;
|
|
1086
915
|
}
|
|
1087
916
|
.log-output .log-cmd {
|
|
1088
|
-
color: var(--accent);
|
|
917
|
+
color: var(--accent-text);
|
|
1089
918
|
}
|
|
1090
919
|
.log-output .log-err {
|
|
1091
|
-
color: var(--
|
|
920
|
+
color: var(--bad);
|
|
1092
921
|
}
|
|
1093
922
|
.log-output .log-done {
|
|
1094
923
|
color: var(--muted);
|
|
@@ -1101,13 +930,12 @@
|
|
|
1101
930
|
align-items: center;
|
|
1102
931
|
gap: 8px;
|
|
1103
932
|
padding: 8px 12px;
|
|
1104
|
-
background: color-mix(in srgb, var(--
|
|
1105
|
-
border-top: 1px solid
|
|
1106
|
-
color-mix(in srgb, var(--yellow, #d97706) 40%, transparent);
|
|
933
|
+
background: color-mix(in srgb, var(--warn) 12%, var(--bg));
|
|
934
|
+
border-top: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
|
|
1107
935
|
}
|
|
1108
936
|
.aws-auth-msg {
|
|
1109
937
|
font-size: 12px;
|
|
1110
|
-
color: var(--
|
|
938
|
+
color: var(--warn);
|
|
1111
939
|
flex: 1;
|
|
1112
940
|
}
|
|
1113
941
|
.aws-auth-btn {
|
|
@@ -1115,8 +943,8 @@
|
|
|
1115
943
|
padding: 3px 10px;
|
|
1116
944
|
border-radius: 4px;
|
|
1117
945
|
cursor: pointer;
|
|
1118
|
-
background: var(--
|
|
1119
|
-
color:
|
|
946
|
+
background: var(--warn);
|
|
947
|
+
color: var(--bg);
|
|
1120
948
|
border: none;
|
|
1121
949
|
font-weight: 600;
|
|
1122
950
|
}
|
|
@@ -1131,7 +959,7 @@
|
|
|
1131
959
|
font-family: var(--mono);
|
|
1132
960
|
font-size: 11px;
|
|
1133
961
|
line-height: 1.5;
|
|
1134
|
-
background: var(--
|
|
962
|
+
background: var(--sunken);
|
|
1135
963
|
border-radius: 4px;
|
|
1136
964
|
white-space: pre-wrap;
|
|
1137
965
|
max-height: 120px;
|
|
@@ -1166,24 +994,14 @@
|
|
|
1166
994
|
</style>
|
|
1167
995
|
</head>
|
|
1168
996
|
<body>
|
|
1169
|
-
<!--
|
|
1170
|
-
<div
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
/>
|
|
1178
|
-
<h1>__DEN_CLI__</h1>
|
|
1179
|
-
<button
|
|
1180
|
-
class="v-pill"
|
|
1181
|
-
id="version-pill"
|
|
1182
|
-
onclick="openChangelog()"
|
|
1183
|
-
title="View changelog"
|
|
1184
|
-
>
|
|
1185
|
-
<span class="v-pip" id="v-pip"></span><span class="v-txt">v…</span>
|
|
1186
|
-
</button>
|
|
997
|
+
<!-- The shell: top bar and rail, the same on every page (shell.js). -->
|
|
998
|
+
<div id="den-shell" data-page="tests"></div>
|
|
999
|
+
<script src="/shell.js"></script>
|
|
1000
|
+
|
|
1001
|
+
<!-- The page's one main landmark: everything after the shell. -->
|
|
1002
|
+
<main id="den-content">
|
|
1003
|
+
<!-- ── Bar 2: context actions (the env picker is the shell's) ───────────── -->
|
|
1004
|
+
<div class="subbar">
|
|
1187
1005
|
<a
|
|
1188
1006
|
id="app-link"
|
|
1189
1007
|
href="#"
|
|
@@ -1198,39 +1016,6 @@
|
|
|
1198
1016
|
"
|
|
1199
1017
|
>↗ open app</a
|
|
1200
1018
|
>
|
|
1201
|
-
<span id="topbar-env-badge"></span>
|
|
1202
|
-
<span
|
|
1203
|
-
id="ws-pill"
|
|
1204
|
-
style="
|
|
1205
|
-
font-size: 11px;
|
|
1206
|
-
font-family: var(--mono);
|
|
1207
|
-
padding: 2px 8px;
|
|
1208
|
-
border-radius: 10px;
|
|
1209
|
-
border: 1px solid #388bfd44;
|
|
1210
|
-
color: #58a6ff;
|
|
1211
|
-
background: #58a6ff10;
|
|
1212
|
-
display: none;
|
|
1213
|
-
"
|
|
1214
|
-
></span>
|
|
1215
|
-
<div class="health" id="health" title="Service health"></div>
|
|
1216
|
-
<div class="spacer"></div>
|
|
1217
|
-
<nav>
|
|
1218
|
-
<a href="/">Local Dev</a>
|
|
1219
|
-
<a href="/services">Services</a>
|
|
1220
|
-
<a href="/environments">Environments</a>
|
|
1221
|
-
<a href="/release">Releases</a>
|
|
1222
|
-
<a href="/logs">Logs <span class="badge" id="log-badge"></span></a>
|
|
1223
|
-
<a href="/tests" id="nav-tests" class="active">Tests</a>
|
|
1224
|
-
</nav>
|
|
1225
|
-
</div>
|
|
1226
|
-
|
|
1227
|
-
<!-- ── Bar 2: env selector (universal) + context actions (right) ──────────── -->
|
|
1228
|
-
<div class="subbar">
|
|
1229
|
-
<div class="env-badge">
|
|
1230
|
-
<select id="env-sel" title="Environment">
|
|
1231
|
-
<option value="local">local</option>
|
|
1232
|
-
</select>
|
|
1233
|
-
</div>
|
|
1234
1019
|
|
|
1235
1020
|
<!-- Tests context: run status + layout toggle + run button -->
|
|
1236
1021
|
<div class="bar2-slot active" id="bar2-tests">
|
|
@@ -1325,35 +1110,6 @@
|
|
|
1325
1110
|
</div>
|
|
1326
1111
|
|
|
1327
1112
|
<div class="test-sources-bar" id="test-sources-bar"></div>
|
|
1328
|
-
|
|
1329
|
-
<div class="output-pane">
|
|
1330
|
-
<div class="pane-bar">
|
|
1331
|
-
<span>Output</span>
|
|
1332
|
-
<button id="clear-test-output">Clear</button>
|
|
1333
|
-
<button
|
|
1334
|
-
id="copy-test-output"
|
|
1335
|
-
title="Copy to clipboard"
|
|
1336
|
-
style="padding: 2px 5px; line-height: 1"
|
|
1337
|
-
>
|
|
1338
|
-
<svg
|
|
1339
|
-
width="13"
|
|
1340
|
-
height="13"
|
|
1341
|
-
viewBox="0 0 13 13"
|
|
1342
|
-
fill="none"
|
|
1343
|
-
stroke="currentColor"
|
|
1344
|
-
stroke-width="1.3"
|
|
1345
|
-
stroke-linecap="round"
|
|
1346
|
-
stroke-linejoin="round"
|
|
1347
|
-
>
|
|
1348
|
-
<rect x="4" y="0.5" width="8.5" height="8.5" rx="1.5" />
|
|
1349
|
-
<path
|
|
1350
|
-
d="M8.5 9v2.5a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h2.5"
|
|
1351
|
-
/>
|
|
1352
|
-
</svg>
|
|
1353
|
-
</button>
|
|
1354
|
-
</div>
|
|
1355
|
-
<pre id="test-output"></pre>
|
|
1356
|
-
</div>
|
|
1357
1113
|
</div>
|
|
1358
1114
|
|
|
1359
1115
|
<div class="divider" id="divider"></div>
|
|
@@ -1391,15 +1147,7 @@
|
|
|
1391
1147
|
<div class="empty-state">Loading services…</div>
|
|
1392
1148
|
</div>
|
|
1393
1149
|
|
|
1394
|
-
<!--
|
|
1395
|
-
<div class="cmd-strip hidden" id="cmd-strip">
|
|
1396
|
-
<div class="pane-bar">
|
|
1397
|
-
<span id="cmd-strip-title">Command Output</span>
|
|
1398
|
-
<button id="stop-cmd" style="display: none">Stop</button>
|
|
1399
|
-
<button id="clear-cmd-output">Clear</button>
|
|
1400
|
-
</div>
|
|
1401
|
-
<pre id="cmd-output"></pre>
|
|
1402
|
-
</div>
|
|
1150
|
+
<!-- A command's output is a tab in the shell's runs dock (R16). -->
|
|
1403
1151
|
</div>
|
|
1404
1152
|
|
|
1405
1153
|
<script type="module">
|
|
@@ -1411,8 +1159,9 @@
|
|
|
1411
1159
|
initLogBadge,
|
|
1412
1160
|
storeOpenLog,
|
|
1413
1161
|
initChangelog,
|
|
1414
|
-
|
|
1162
|
+
envReady,
|
|
1415
1163
|
} from "/header.js";
|
|
1164
|
+
import { ownersLabel } from "/workspace-picker.js";
|
|
1416
1165
|
import {
|
|
1417
1166
|
buildReportPaneModel,
|
|
1418
1167
|
getNextReportViewOnPillarChange,
|
|
@@ -1444,15 +1193,9 @@
|
|
|
1444
1193
|
// ── Init ──────────────────────────────────────────────────────────────────
|
|
1445
1194
|
async function init() {
|
|
1446
1195
|
// Populate env selector
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
.catch(() => ["local"]);
|
|
1196
|
+
// The shell's env picker (header.js), one for every page.
|
|
1197
|
+
await envReady();
|
|
1450
1198
|
const envSel = document.getElementById("env-sel");
|
|
1451
|
-
envSel.innerHTML = envs
|
|
1452
|
-
.map((e) => `<option value="${e}">${e}</option>`)
|
|
1453
|
-
.join("");
|
|
1454
|
-
const savedEnv = localStorage.getItem("__DEN_CLI__-env");
|
|
1455
|
-
if (savedEnv && envs.includes(savedEnv)) envSel.value = savedEnv;
|
|
1456
1199
|
updateAppLink(envSel.value);
|
|
1457
1200
|
|
|
1458
1201
|
// Restore pillar
|
|
@@ -1480,7 +1223,7 @@
|
|
|
1480
1223
|
await Promise.all([
|
|
1481
1224
|
loadModules(pillar),
|
|
1482
1225
|
initHealth(),
|
|
1483
|
-
initWorkspacePill(
|
|
1226
|
+
initWorkspacePill(),
|
|
1484
1227
|
applyDenConfig(),
|
|
1485
1228
|
initChangelog(),
|
|
1486
1229
|
]);
|
|
@@ -1490,18 +1233,6 @@
|
|
|
1490
1233
|
routeFromHash();
|
|
1491
1234
|
}
|
|
1492
1235
|
|
|
1493
|
-
// ── Workspace ─────────────────────────────────────────────────────────────
|
|
1494
|
-
// Module discovery, test sources and the run itself all resolve repos from
|
|
1495
|
-
// the FOCUSED workspace server-side (discoverRepos → getActiveWorkspace), so
|
|
1496
|
-
// switching focus has to re-pull everything this page derives from it.
|
|
1497
|
-
async function onWorkspaceSwitch() {
|
|
1498
|
-
await loadModules(pillar);
|
|
1499
|
-
refreshHealth(document.getElementById("env-sel").value, {
|
|
1500
|
-
force: true,
|
|
1501
|
-
});
|
|
1502
|
-
if (currentView === "services") loadServices();
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
1236
|
// ── Routing ───────────────────────────────────────────────────────────────
|
|
1506
1237
|
window.addEventListener("hashchange", routeFromHash);
|
|
1507
1238
|
|
|
@@ -1530,7 +1261,6 @@
|
|
|
1530
1261
|
// ── App link ─────────────────────────────────────────────────────────────
|
|
1531
1262
|
async function updateAppLink(env) {
|
|
1532
1263
|
const link = document.getElementById("app-link");
|
|
1533
|
-
const badge = document.getElementById("topbar-env-badge");
|
|
1534
1264
|
if (!link) return;
|
|
1535
1265
|
try {
|
|
1536
1266
|
const { url } = await fetch(`/api/app-url?env=${env}`).then((r) =>
|
|
@@ -1540,24 +1270,17 @@
|
|
|
1540
1270
|
link.href = url;
|
|
1541
1271
|
link.style.display = "";
|
|
1542
1272
|
link.title = url;
|
|
1543
|
-
if (badge) {
|
|
1544
|
-
badge.textContent = env;
|
|
1545
|
-
badge.style.display = "inline-flex";
|
|
1546
|
-
}
|
|
1547
1273
|
} else {
|
|
1548
1274
|
link.style.display = "none";
|
|
1549
|
-
if (badge) badge.style.display = "none";
|
|
1550
1275
|
}
|
|
1551
1276
|
} catch {
|
|
1552
1277
|
link.style.display = "none";
|
|
1553
|
-
if (badge) badge.style.display = "none";
|
|
1554
1278
|
}
|
|
1555
1279
|
}
|
|
1556
1280
|
|
|
1557
1281
|
// ── Env ───────────────────────────────────────────────────────────────────
|
|
1558
1282
|
document.getElementById("env-sel").addEventListener("change", () => {
|
|
1559
1283
|
const env = document.getElementById("env-sel").value;
|
|
1560
|
-
localStorage.setItem("__DEN_CLI__-env", env);
|
|
1561
1284
|
updateAppLink(env);
|
|
1562
1285
|
|
|
1563
1286
|
// Update health dots for the new env immediately
|
|
@@ -2090,6 +1813,12 @@
|
|
|
2090
1813
|
setRunStatus("running", "running\u2026");
|
|
2091
1814
|
document.getElementById("run-btn").disabled = true;
|
|
2092
1815
|
resetReportPane();
|
|
1816
|
+
// The output is one tab in the shell's runs dock (R16), kept across
|
|
1817
|
+
// runs as the pane was; Clear there also clears the raw report.
|
|
1818
|
+
runs().start(TEST_RUN, `tests ${pillar}`, {
|
|
1819
|
+
doneLine: false,
|
|
1820
|
+
onClear: clearTestOutput,
|
|
1821
|
+
});
|
|
2093
1822
|
testAppendLine(
|
|
2094
1823
|
`\n\u25b6 ${pillar}${label ? " / " + label : ""} \u00b7 ${env}\n`,
|
|
2095
1824
|
"cmd",
|
|
@@ -2115,6 +1844,7 @@
|
|
|
2115
1844
|
ok ? "passed" : "failed",
|
|
2116
1845
|
ok ? `passed (${dur})` : `failed (${dur})`,
|
|
2117
1846
|
);
|
|
1847
|
+
runs().finish(TEST_RUN, { ok });
|
|
2118
1848
|
document.getElementById("run-btn").disabled = false;
|
|
2119
1849
|
testEs.close();
|
|
2120
1850
|
testEs = null;
|
|
@@ -2141,6 +1871,7 @@
|
|
|
2141
1871
|
};
|
|
2142
1872
|
testEs.onerror = () => {
|
|
2143
1873
|
testAppendLine("Connection closed", "err");
|
|
1874
|
+
runs().finish(TEST_RUN, { ok: false });
|
|
2144
1875
|
setRunStatus("failed", "error");
|
|
2145
1876
|
document.getElementById("run-btn").disabled = false;
|
|
2146
1877
|
testEs?.close();
|
|
@@ -2148,12 +1879,12 @@
|
|
|
2148
1879
|
};
|
|
2149
1880
|
}
|
|
2150
1881
|
|
|
2151
|
-
const
|
|
1882
|
+
const TEST_RUN = "tests";
|
|
1883
|
+
const runs = () => window.denShell.runs;
|
|
2152
1884
|
const _k6ProgressRe = /^(\S+)\s+[\[✓✗]/;
|
|
2153
1885
|
const _k6RunningRe = /^running \(\d+m[\d.]+s\), \d+\/\d+ VUs,/;
|
|
2154
1886
|
|
|
2155
1887
|
function testAppendLine(text, cls) {
|
|
2156
|
-
const out = document.getElementById("test-output");
|
|
2157
1888
|
const raw = document.getElementById("report-raw-output");
|
|
2158
1889
|
|
|
2159
1890
|
// k6 "running (...)" status lines are redundant with the progress bar;
|
|
@@ -2163,60 +1894,24 @@
|
|
|
2163
1894
|
return;
|
|
2164
1895
|
}
|
|
2165
1896
|
|
|
2166
|
-
// k6 progress/result lines:
|
|
2167
|
-
// so the UI shows a compact live-updating
|
|
1897
|
+
// k6 progress/result lines: one line per scenario, updated in place
|
|
1898
|
+
// (a keyed line in the dock), so the UI shows a compact live-updating
|
|
1899
|
+
// progress bar.
|
|
2168
1900
|
const pm = !cls && text.match(_k6ProgressRe);
|
|
2169
1901
|
if (pm) {
|
|
2170
|
-
|
|
2171
|
-
if (_progressSpans[key]) {
|
|
2172
|
-
_progressSpans[key].textContent = text + "\n";
|
|
2173
|
-
out.scrollTop = out.scrollHeight;
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
const span = document.createElement("span");
|
|
2177
|
-
span.className = "k6-progress";
|
|
2178
|
-
span.textContent = text + "\n";
|
|
2179
|
-
_progressSpans[key] = span;
|
|
2180
|
-
out.appendChild(span);
|
|
2181
|
-
out.scrollTop = out.scrollHeight;
|
|
1902
|
+
runs().append(TEST_RUN, text, "k6-progress", { key: pm[1] });
|
|
2182
1903
|
return;
|
|
2183
1904
|
}
|
|
2184
1905
|
|
|
2185
|
-
|
|
2186
|
-
if (cls) span.className = cls;
|
|
2187
|
-
span.textContent = text + "\n";
|
|
2188
|
-
out.appendChild(span);
|
|
2189
|
-
out.scrollTop = out.scrollHeight;
|
|
1906
|
+
runs().append(TEST_RUN, text, cls);
|
|
2190
1907
|
raw.textContent += text + "\n";
|
|
2191
1908
|
raw.scrollTop = raw.scrollHeight;
|
|
2192
1909
|
}
|
|
2193
1910
|
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
.addEventListener("click", () => {
|
|
2197
|
-
const text = document.getElementById("test-output").innerText;
|
|
2198
|
-
const btn = document.getElementById("copy-test-output");
|
|
2199
|
-
const icon =
|
|
2200
|
-
'<svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="0.5" width="8.5" height="8.5" rx="1.5"/><path d="M8.5 9v2.5a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h2.5"/></svg>';
|
|
2201
|
-
const check =
|
|
2202
|
-
'<svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="var(--green)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 7l3 3 6-7"/></svg>';
|
|
2203
|
-
copyToClipboard(text)
|
|
2204
|
-
.then(() => {
|
|
2205
|
-
btn.innerHTML = check;
|
|
2206
|
-
setTimeout(() => {
|
|
2207
|
-
btn.innerHTML = icon;
|
|
2208
|
-
}, 1500);
|
|
2209
|
-
})
|
|
2210
|
-
.catch(() => {});
|
|
2211
|
-
});
|
|
2212
|
-
|
|
2213
|
-
document
|
|
2214
|
-
.getElementById("clear-test-output")
|
|
2215
|
-
.addEventListener("click", () => {
|
|
2216
|
-
document.getElementById("test-output").innerHTML = "";
|
|
1911
|
+
/** The dock's Clear on the tests tab: the raw report output too. */
|
|
1912
|
+
function clearTestOutput() {
|
|
2217
1913
|
document.getElementById("report-raw-output").textContent = "";
|
|
2218
|
-
|
|
2219
|
-
});
|
|
1914
|
+
}
|
|
2220
1915
|
|
|
2221
1916
|
function setRunStatus(state, text) {
|
|
2222
1917
|
document.getElementById("run-dot").className = `dot ${state}`;
|
|
@@ -2449,7 +2144,7 @@
|
|
|
2449
2144
|
<span class="dot" id="svc-dot-${s.repo}-${s.service}"></span>
|
|
2450
2145
|
<div>
|
|
2451
2146
|
<div class="service-name">${s.service}</div>
|
|
2452
|
-
<div class="service-repo">${s.repo}</div>
|
|
2147
|
+
<div class="service-repo">${s.repo}${ownersLabel(s.inWorkspaces)}</div>
|
|
2453
2148
|
</div>
|
|
2454
2149
|
</div>
|
|
2455
2150
|
<div class="cmd-row">
|
|
@@ -2477,12 +2172,23 @@
|
|
|
2477
2172
|
if (el) el.className = `dot ${state}`;
|
|
2478
2173
|
}
|
|
2479
2174
|
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2175
|
+
// The running command's tab carries Stop (Detach for a daemon) in the
|
|
2176
|
+
// dock's bar while it runs.
|
|
2177
|
+
let execRun = null;
|
|
2178
|
+
let execDetachable = true;
|
|
2179
|
+
function stopAction() {
|
|
2180
|
+
return [
|
|
2181
|
+
{
|
|
2182
|
+
id: "stop",
|
|
2183
|
+
label: execDetachable ? "Detach" : "Stop",
|
|
2184
|
+
run: stopCmd,
|
|
2185
|
+
},
|
|
2186
|
+
];
|
|
2187
|
+
}
|
|
2188
|
+
function stopCmd() {
|
|
2189
|
+
const detachable = execDetachable;
|
|
2483
2190
|
execEs?.close();
|
|
2484
2191
|
execEs = null;
|
|
2485
|
-
sb.style.display = "none";
|
|
2486
2192
|
document
|
|
2487
2193
|
.querySelectorAll('.cmd-btn[data-type="exec"]')
|
|
2488
2194
|
.forEach((b) => (b.disabled = false));
|
|
@@ -2491,7 +2197,8 @@
|
|
|
2491
2197
|
detachable ? "--- Detached ---" : "--- Stopped ---",
|
|
2492
2198
|
"err",
|
|
2493
2199
|
);
|
|
2494
|
-
|
|
2200
|
+
if (execRun) runs().set(execRun, { running: false, actions: [] });
|
|
2201
|
+
}
|
|
2495
2202
|
|
|
2496
2203
|
// ── Service exec ──────────────────────────────────────────────────────────
|
|
2497
2204
|
function runExec(repo, service, cmd) {
|
|
@@ -2505,15 +2212,19 @@
|
|
|
2505
2212
|
document
|
|
2506
2213
|
.querySelectorAll('.cmd-btn[data-type="exec"]')
|
|
2507
2214
|
.forEach((b) => (b.disabled = true));
|
|
2508
|
-
const stopBtn = document.getElementById("stop-cmd");
|
|
2509
|
-
stopBtn.style.display = "";
|
|
2510
|
-
stopBtn.textContent = "Stop";
|
|
2511
2215
|
setSvcDot(repo, service, "running");
|
|
2512
2216
|
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2217
|
+
// The run's tab in the dock: one per service, the latest command.
|
|
2218
|
+
if (execRun) runs().set(execRun, { running: false, actions: [] });
|
|
2219
|
+
execDetachable = false;
|
|
2220
|
+
execRun = runs().start(
|
|
2221
|
+
`${repo}/${service}`,
|
|
2222
|
+
`${cmd} ${repo}/${service}`,
|
|
2223
|
+
{
|
|
2224
|
+
doneLine: false,
|
|
2225
|
+
actions: [{ id: "stop", label: "Stop", run: stopCmd }],
|
|
2226
|
+
},
|
|
2227
|
+
);
|
|
2517
2228
|
document.getElementById("svc-status").textContent =
|
|
2518
2229
|
`running ${service}:${cmd}\u2026`;
|
|
2519
2230
|
|
|
@@ -2528,9 +2239,8 @@
|
|
|
2528
2239
|
const msg = JSON.parse(e.data);
|
|
2529
2240
|
if (msg.type === "cmd") {
|
|
2530
2241
|
cmdAppendLine(msg.text, "cmd");
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
sb.dataset.detachable = msg.detached ? "1" : "0";
|
|
2242
|
+
execDetachable = !!msg.detached;
|
|
2243
|
+
runs().set(execRun, { actions: stopAction() });
|
|
2534
2244
|
} else if (msg.type === "line") {
|
|
2535
2245
|
cmdAppendLine(msg.text, "");
|
|
2536
2246
|
} else if (msg.type === "error") {
|
|
@@ -2544,7 +2254,8 @@
|
|
|
2544
2254
|
);
|
|
2545
2255
|
setSvcDot(repo, service, ok ? "ok" : "err");
|
|
2546
2256
|
document.getElementById("svc-status").textContent = "idle";
|
|
2547
|
-
|
|
2257
|
+
runs().set(execRun, { actions: [] });
|
|
2258
|
+
runs().finish(execRun, { ok });
|
|
2548
2259
|
document
|
|
2549
2260
|
.querySelectorAll('.cmd-btn[data-type="exec"]')
|
|
2550
2261
|
.forEach((b) => (b.disabled = false));
|
|
@@ -2556,7 +2267,8 @@
|
|
|
2556
2267
|
cmdAppendLine("Connection closed", "err");
|
|
2557
2268
|
setSvcDot(repo, service, "err");
|
|
2558
2269
|
document.getElementById("svc-status").textContent = "idle";
|
|
2559
|
-
|
|
2270
|
+
runs().set(execRun, { actions: [] });
|
|
2271
|
+
runs().finish(execRun, { ok: false });
|
|
2560
2272
|
document
|
|
2561
2273
|
.querySelectorAll('.cmd-btn[data-type="exec"]')
|
|
2562
2274
|
.forEach((b) => (b.disabled = false));
|
|
@@ -2566,21 +2278,9 @@
|
|
|
2566
2278
|
}
|
|
2567
2279
|
|
|
2568
2280
|
function cmdAppendLine(text, cls) {
|
|
2569
|
-
|
|
2570
|
-
const span = document.createElement("span");
|
|
2571
|
-
if (cls) span.className = cls;
|
|
2572
|
-
span.textContent = text + "\n";
|
|
2573
|
-
out.appendChild(span);
|
|
2574
|
-
out.scrollTop = out.scrollHeight;
|
|
2281
|
+
if (execRun) runs().append(execRun, text, cls);
|
|
2575
2282
|
}
|
|
2576
2283
|
|
|
2577
|
-
document
|
|
2578
|
-
.getElementById("clear-cmd-output")
|
|
2579
|
-
.addEventListener("click", () => {
|
|
2580
|
-
document.getElementById("cmd-output").innerHTML = "";
|
|
2581
|
-
document.getElementById("cmd-strip").classList.add("hidden");
|
|
2582
|
-
});
|
|
2583
|
-
|
|
2584
2284
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
2585
2285
|
function fmtDuration(ms) {
|
|
2586
2286
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -2591,5 +2291,6 @@
|
|
|
2591
2291
|
|
|
2592
2292
|
init();
|
|
2593
2293
|
</script>
|
|
2294
|
+
</main>
|
|
2594
2295
|
</body>
|
|
2595
2296
|
</html>
|