@jobshimo/browser-link 0.43.0 → 0.44.1
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 +26 -2
- package/dist/bridge/ws-bridge.d.ts +6 -2
- package/dist/bridge/ws-bridge.js +34 -27
- package/dist/bridge/ws-bridge.js.map +1 -1
- package/dist/cdp/inpage/observer.d.ts +2 -2
- package/dist/cdp/inpage/observer.js +11 -4
- package/dist/cdp/inpage/observer.js.map +1 -1
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +32 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +15 -0
- package/dist/commands/config.js +55 -5
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/doctor.d.ts +20 -1
- package/dist/commands/doctor.js +41 -19
- package/dist/commands/doctor.js.map +1 -1
- package/dist/config.d.ts +20 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -1
- package/dist/downloads.d.ts +10 -3
- package/dist/downloads.js +23 -4
- package/dist/downloads.js.map +1 -1
- package/dist/extension/activity.html +30 -4
- package/dist/extension/admin.d.ts +1 -0
- package/dist/extension/admin.html +290 -0
- package/dist/extension/admin.js +210 -0
- package/dist/extension/admin.js.map +1 -0
- package/dist/extension/background.js +41 -0
- package/dist/extension/background.js.map +1 -1
- package/dist/extension/inpage/observer.d.ts +2 -2
- package/dist/extension/inpage/observer.js +11 -4
- package/dist/extension/inpage/observer.js.map +1 -1
- package/dist/extension/manifest.json +1 -1
- package/dist/extension/popup.html +28 -119
- package/dist/extension/popup.js +12 -205
- package/dist/extension/popup.js.map +1 -1
- package/dist/lifecycle/election.d.ts +16 -5
- package/dist/lifecycle/election.js +21 -4
- package/dist/lifecycle/election.js.map +1 -1
- package/dist/lifecycle/processes.d.ts +18 -0
- package/dist/lifecycle/processes.js +40 -7
- package/dist/lifecycle/processes.js.map +1 -1
- package/dist/lifecycle/repair.d.ts +15 -3
- package/dist/lifecycle/repair.js +41 -8
- package/dist/lifecycle/repair.js.map +1 -1
- package/dist/lifecycle/version-endpoint.d.ts +8 -3
- package/dist/lifecycle/version-endpoint.js +19 -4
- package/dist/lifecycle/version-endpoint.js.map +1 -1
- package/dist/manual/refresh.js +6 -0
- package/dist/manual/refresh.js.map +1 -1
- package/dist/permissions.js +6 -0
- package/dist/permissions.js.map +1 -1
- package/dist/server.js +8 -4
- package/dist/server.js.map +1 -1
- package/dist/tools/browser-definitions.js +6 -1
- package/dist/tools/browser-definitions.js.map +1 -1
- package/dist/tools/browser-dispatch.js +28 -4
- package/dist/tools/browser-dispatch.js.map +1 -1
- package/package.json +1 -1
|
@@ -392,16 +392,37 @@
|
|
|
392
392
|
|
|
393
393
|
/* ---------- table ---------- */
|
|
394
394
|
|
|
395
|
+
/* A horizontal bar only when the window is genuinely narrower than the
|
|
396
|
+
* table's minimum: the fixed layout below sizes columns from these
|
|
397
|
+
* widths, not from the longest cell, so ordinary rows always fit. */
|
|
395
398
|
.scroll {
|
|
396
399
|
flex: 1 1 auto;
|
|
397
|
-
|
|
400
|
+
min-width: 0;
|
|
401
|
+
overflow-x: auto;
|
|
402
|
+
overflow-y: auto;
|
|
398
403
|
}
|
|
399
404
|
|
|
400
405
|
table {
|
|
406
|
+
table-layout: fixed;
|
|
401
407
|
width: 100%;
|
|
408
|
+
min-width: 760px;
|
|
402
409
|
border-collapse: collapse;
|
|
403
410
|
}
|
|
404
411
|
|
|
412
|
+
/* When · Agent · Tool · Target · Result — Target takes the rest. */
|
|
413
|
+
thead th:nth-child(1) {
|
|
414
|
+
width: 112px;
|
|
415
|
+
}
|
|
416
|
+
thead th:nth-child(2) {
|
|
417
|
+
width: 150px;
|
|
418
|
+
}
|
|
419
|
+
thead th:nth-child(3) {
|
|
420
|
+
width: 200px;
|
|
421
|
+
}
|
|
422
|
+
thead th:nth-child(5) {
|
|
423
|
+
width: 190px;
|
|
424
|
+
}
|
|
425
|
+
|
|
405
426
|
thead th {
|
|
406
427
|
position: sticky;
|
|
407
428
|
top: 0;
|
|
@@ -432,9 +453,13 @@
|
|
|
432
453
|
background: var(--error-soft);
|
|
433
454
|
}
|
|
434
455
|
|
|
456
|
+
/* The exact time is in the cell's title, so a long locale format may
|
|
457
|
+
* end in an ellipsis rather than widen the column. */
|
|
435
458
|
.when {
|
|
436
459
|
color: var(--text-muted);
|
|
437
460
|
white-space: nowrap;
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
text-overflow: ellipsis;
|
|
438
463
|
font-variant-numeric: tabular-nums;
|
|
439
464
|
}
|
|
440
465
|
|
|
@@ -446,12 +471,13 @@
|
|
|
446
471
|
font-weight: 600;
|
|
447
472
|
background: var(--brand-ring);
|
|
448
473
|
color: var(--text);
|
|
449
|
-
|
|
474
|
+
max-width: 100%;
|
|
475
|
+
overflow-wrap: anywhere;
|
|
450
476
|
}
|
|
451
477
|
|
|
452
478
|
.tool {
|
|
453
479
|
font-weight: 600;
|
|
454
|
-
|
|
480
|
+
overflow-wrap: anywhere;
|
|
455
481
|
}
|
|
456
482
|
|
|
457
483
|
.target,
|
|
@@ -477,8 +503,8 @@
|
|
|
477
503
|
}
|
|
478
504
|
|
|
479
505
|
.outcome {
|
|
480
|
-
white-space: nowrap;
|
|
481
506
|
font-variant-numeric: tabular-nums;
|
|
507
|
+
overflow-wrap: anywhere;
|
|
482
508
|
}
|
|
483
509
|
|
|
484
510
|
.outcome.ok {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,290 @@
|
|
|
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" />
|
|
6
|
+
<title>browser-link — Admin</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* Same slate-blue system as popup.html, with the Activity window's dark
|
|
9
|
+
* variant: this window, like Activity, stays open on screen. Only the
|
|
10
|
+
* tokens change between themes — every rule below is written once. */
|
|
11
|
+
:root {
|
|
12
|
+
--brand-from: #5b8def;
|
|
13
|
+
--brand-to: #2a5fd6;
|
|
14
|
+
--brand-ring: rgba(29, 78, 216, 0.32);
|
|
15
|
+
|
|
16
|
+
--bg: #ffffff;
|
|
17
|
+
--bg-soft: #f8fafc;
|
|
18
|
+
--bg-raised: #ffffff;
|
|
19
|
+
--border: #e2e8f0;
|
|
20
|
+
--border-soft: #eef2f7;
|
|
21
|
+
|
|
22
|
+
--text: #0f172a;
|
|
23
|
+
--text-muted: #64748b;
|
|
24
|
+
--text-faint: #94a3b8;
|
|
25
|
+
|
|
26
|
+
--error-fg: #b91c1c;
|
|
27
|
+
--error-soft: #fef2f2;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@media (prefers-color-scheme: dark) {
|
|
31
|
+
:root {
|
|
32
|
+
--bg: #0b1120;
|
|
33
|
+
--bg-soft: #111a2e;
|
|
34
|
+
--bg-raised: #131d33;
|
|
35
|
+
--border: #1e293b;
|
|
36
|
+
--border-soft: #172033;
|
|
37
|
+
|
|
38
|
+
--text: #e2e8f0;
|
|
39
|
+
--text-muted: #94a3b8;
|
|
40
|
+
--text-faint: #64748b;
|
|
41
|
+
|
|
42
|
+
--error-fg: #f87171;
|
|
43
|
+
--error-soft: rgba(239, 68, 68, 0.12);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
* {
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
html,
|
|
52
|
+
body {
|
|
53
|
+
margin: 0;
|
|
54
|
+
padding: 0;
|
|
55
|
+
font-family:
|
|
56
|
+
ui-sans-serif,
|
|
57
|
+
-apple-system,
|
|
58
|
+
BlinkMacSystemFont,
|
|
59
|
+
'Inter',
|
|
60
|
+
'Segoe UI',
|
|
61
|
+
system-ui,
|
|
62
|
+
sans-serif;
|
|
63
|
+
font-size: 13px;
|
|
64
|
+
color: var(--text);
|
|
65
|
+
background: var(--bg);
|
|
66
|
+
-webkit-font-smoothing: antialiased;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
header {
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 12px;
|
|
73
|
+
padding: 14px 18px;
|
|
74
|
+
border-bottom: 1px solid var(--border);
|
|
75
|
+
background: var(--bg-raised);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.mark {
|
|
79
|
+
width: 26px;
|
|
80
|
+
height: 26px;
|
|
81
|
+
border-radius: 8px;
|
|
82
|
+
background: linear-gradient(135deg, var(--brand-from), var(--brand-to));
|
|
83
|
+
box-shadow: 0 0 0 3px var(--brand-ring);
|
|
84
|
+
flex: 0 0 auto;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.titles {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
gap: 1px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
h1 {
|
|
94
|
+
margin: 0;
|
|
95
|
+
font-size: 14px;
|
|
96
|
+
font-weight: 650;
|
|
97
|
+
letter-spacing: -0.01em;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.subtitle {
|
|
101
|
+
font-size: 11.5px;
|
|
102
|
+
color: var(--text-muted);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main {
|
|
106
|
+
padding: 6px 18px 18px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
button {
|
|
110
|
+
width: 100%;
|
|
111
|
+
padding: 10px 12px;
|
|
112
|
+
border: none;
|
|
113
|
+
border-radius: 8px;
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
font: inherit;
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
button.primary {
|
|
120
|
+
background: linear-gradient(160deg, var(--brand-from) 0%, var(--brand-to) 100%);
|
|
121
|
+
color: #ffffff;
|
|
122
|
+
box-shadow: 0 6px 14px var(--brand-ring);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
button.danger {
|
|
126
|
+
background: var(--bg);
|
|
127
|
+
color: var(--error-fg);
|
|
128
|
+
box-shadow: inset 0 0 0 1px var(--error-fg);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
button.danger:hover:not(:disabled) {
|
|
132
|
+
background: var(--error-soft);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
button:disabled {
|
|
136
|
+
opacity: 0.55;
|
|
137
|
+
cursor: progress;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
button:focus-visible,
|
|
141
|
+
select:focus-visible,
|
|
142
|
+
input:focus-visible {
|
|
143
|
+
outline: 2px solid var(--brand-from);
|
|
144
|
+
outline-offset: 1px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.dialog-actions {
|
|
148
|
+
display: grid;
|
|
149
|
+
grid-template-columns: 1fr 1fr;
|
|
150
|
+
gap: 8px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.flows-title {
|
|
154
|
+
font-size: 10.5px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
letter-spacing: 0.06em;
|
|
157
|
+
text-transform: uppercase;
|
|
158
|
+
color: var(--text-faint);
|
|
159
|
+
margin-bottom: 6px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Admin rows — rendered generically from the server's AdminState
|
|
163
|
+
* (admin-view.ts); every label inside comes from the server. */
|
|
164
|
+
.admin-row {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
justify-content: space-between;
|
|
168
|
+
gap: 12px;
|
|
169
|
+
padding: 7px 0;
|
|
170
|
+
border-top: 1px solid var(--border-soft);
|
|
171
|
+
font-size: 12.5px;
|
|
172
|
+
color: var(--text-muted);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.admin-row-label {
|
|
176
|
+
min-width: 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.admin-row-value {
|
|
180
|
+
color: var(--text);
|
|
181
|
+
text-align: right;
|
|
182
|
+
overflow-wrap: anywhere;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
a.admin-row-value {
|
|
186
|
+
color: var(--brand-to);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.admin-row select,
|
|
190
|
+
.admin-row button {
|
|
191
|
+
width: auto;
|
|
192
|
+
font-family: inherit;
|
|
193
|
+
font-size: 12px;
|
|
194
|
+
font-weight: 500;
|
|
195
|
+
color: var(--text);
|
|
196
|
+
background: var(--bg-soft);
|
|
197
|
+
border: 1px solid var(--border);
|
|
198
|
+
border-radius: 6px;
|
|
199
|
+
padding: 5px 8px;
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.admin-row button:hover:not(:disabled),
|
|
204
|
+
.admin-row select:hover:not(:disabled) {
|
|
205
|
+
border-color: var(--brand-from);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.admin-row input[type='checkbox'] {
|
|
209
|
+
width: 15px;
|
|
210
|
+
height: 15px;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
accent-color: var(--brand-to);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.admin-note {
|
|
216
|
+
padding-bottom: 4px;
|
|
217
|
+
font-size: 11.5px;
|
|
218
|
+
color: var(--text-faint);
|
|
219
|
+
overflow-wrap: anywhere;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.admin-section-title {
|
|
223
|
+
margin-top: 16px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.admin-status {
|
|
227
|
+
margin-top: 12px;
|
|
228
|
+
color: var(--text);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.admin-status[data-kind='error'] {
|
|
232
|
+
color: var(--error-fg);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.admin-confirm {
|
|
236
|
+
margin-top: 12px;
|
|
237
|
+
padding: 12px 14px;
|
|
238
|
+
border: 1px solid var(--border);
|
|
239
|
+
border-radius: 10px;
|
|
240
|
+
background: var(--bg-soft);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.admin-confirm-preview {
|
|
244
|
+
margin-bottom: 10px;
|
|
245
|
+
color: var(--text);
|
|
246
|
+
overflow-wrap: anywhere;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.admin-output {
|
|
250
|
+
margin: 8px 0 0;
|
|
251
|
+
padding: 8px 10px;
|
|
252
|
+
max-height: 240px;
|
|
253
|
+
overflow: auto;
|
|
254
|
+
font-size: 11.5px;
|
|
255
|
+
background: var(--bg-soft);
|
|
256
|
+
border: 1px solid var(--border);
|
|
257
|
+
border-radius: 6px;
|
|
258
|
+
white-space: pre-wrap;
|
|
259
|
+
}
|
|
260
|
+
</style>
|
|
261
|
+
</head>
|
|
262
|
+
<body>
|
|
263
|
+
<header>
|
|
264
|
+
<div class="mark" aria-hidden="true"></div>
|
|
265
|
+
<div class="titles">
|
|
266
|
+
<h1>Admin</h1>
|
|
267
|
+
<span class="subtitle">browser-link server settings · same as the console</span>
|
|
268
|
+
</div>
|
|
269
|
+
</header>
|
|
270
|
+
|
|
271
|
+
<main>
|
|
272
|
+
<!-- Rendered by admin.ts from the server's AdminState (admin.describe
|
|
273
|
+
over any connected tab's socket): the same sections, options and
|
|
274
|
+
labels as the console. Disk-writing actions show the server's
|
|
275
|
+
preview with Confirm/Cancel before applying. -->
|
|
276
|
+
<div id="admin-confirm" class="admin-confirm" hidden>
|
|
277
|
+
<div id="admin-confirm-preview" class="admin-confirm-preview"></div>
|
|
278
|
+
<div class="dialog-actions">
|
|
279
|
+
<button id="admin-confirm-yes" class="primary" type="button">Confirm</button>
|
|
280
|
+
<button id="admin-confirm-no" class="danger" type="button">Cancel</button>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
<div id="admin-status" class="admin-status" hidden></div>
|
|
284
|
+
<pre id="admin-output" class="admin-output" hidden></pre>
|
|
285
|
+
<div id="admin-body"></div>
|
|
286
|
+
</main>
|
|
287
|
+
|
|
288
|
+
<script type="module" src="admin.js"></script>
|
|
289
|
+
</body>
|
|
290
|
+
</html>
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { FALLBACK_ADMIN_UI, adminUi, buildAdminRows, describeAdminError, initialConfirmState, reduceConfirm, } from './admin-view.js';
|
|
2
|
+
// chrome.runtime.sendMessage returns `Promise<any>`; re-bind through
|
|
3
|
+
// `unknown` so the final `as T` is an explicit, auditable narrowing.
|
|
4
|
+
async function send(payload) {
|
|
5
|
+
const raw = await chrome.runtime.sendMessage(payload);
|
|
6
|
+
return raw;
|
|
7
|
+
}
|
|
8
|
+
function $(id) {
|
|
9
|
+
const el = document.getElementById(id);
|
|
10
|
+
if (!el)
|
|
11
|
+
throw new Error(`Element not found: ${id}`);
|
|
12
|
+
return el;
|
|
13
|
+
}
|
|
14
|
+
let adminConfirm = initialConfirmState();
|
|
15
|
+
let adminStrings = FALLBACK_ADMIN_UI;
|
|
16
|
+
let adminBusy = false;
|
|
17
|
+
function renderAdminEmpty(message) {
|
|
18
|
+
const body = $('admin-body');
|
|
19
|
+
body.replaceChildren();
|
|
20
|
+
const empty = document.createElement('div');
|
|
21
|
+
empty.className = 'admin-note';
|
|
22
|
+
empty.textContent = message;
|
|
23
|
+
body.appendChild(empty);
|
|
24
|
+
}
|
|
25
|
+
function adminControlFor(row) {
|
|
26
|
+
switch (row.control) {
|
|
27
|
+
case 'toggle': {
|
|
28
|
+
const input = document.createElement('input');
|
|
29
|
+
input.type = 'checkbox';
|
|
30
|
+
input.checked = row.checked === true;
|
|
31
|
+
input.disabled = row.disabled === true || adminBusy;
|
|
32
|
+
input.setAttribute('aria-label', row.label);
|
|
33
|
+
input.addEventListener('change', () => {
|
|
34
|
+
void runAdminAction({ id: row.itemId, value: input.checked });
|
|
35
|
+
});
|
|
36
|
+
return input;
|
|
37
|
+
}
|
|
38
|
+
case 'select': {
|
|
39
|
+
const select = document.createElement('select');
|
|
40
|
+
select.setAttribute('aria-label', row.label);
|
|
41
|
+
for (const option of row.options ?? []) {
|
|
42
|
+
const el = document.createElement('option');
|
|
43
|
+
el.value = option.value;
|
|
44
|
+
el.textContent = option.label;
|
|
45
|
+
select.appendChild(el);
|
|
46
|
+
}
|
|
47
|
+
select.value = row.selected ?? '';
|
|
48
|
+
select.disabled = row.disabled === true || adminBusy;
|
|
49
|
+
select.addEventListener('change', () => {
|
|
50
|
+
void runAdminAction({ id: row.itemId, value: select.value });
|
|
51
|
+
});
|
|
52
|
+
return select;
|
|
53
|
+
}
|
|
54
|
+
case 'button': {
|
|
55
|
+
const button = document.createElement('button');
|
|
56
|
+
button.type = 'button';
|
|
57
|
+
button.textContent = row.label;
|
|
58
|
+
button.disabled = row.disabled === true || adminBusy;
|
|
59
|
+
button.addEventListener('click', () => {
|
|
60
|
+
void runAdminAction({ id: row.itemId });
|
|
61
|
+
});
|
|
62
|
+
return button;
|
|
63
|
+
}
|
|
64
|
+
case 'link': {
|
|
65
|
+
// admin-view.ts only sets `control: 'link'` for an http(s) URL.
|
|
66
|
+
const link = document.createElement('a');
|
|
67
|
+
link.href = row.href ?? '';
|
|
68
|
+
link.target = '_blank';
|
|
69
|
+
link.rel = 'noopener noreferrer';
|
|
70
|
+
link.className = 'admin-row-value';
|
|
71
|
+
link.textContent = row.valueText;
|
|
72
|
+
return link;
|
|
73
|
+
}
|
|
74
|
+
default: {
|
|
75
|
+
const value = document.createElement('span');
|
|
76
|
+
value.className = 'admin-row-value';
|
|
77
|
+
value.textContent = row.valueText;
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function renderAdminRows(rows) {
|
|
83
|
+
const body = $('admin-body');
|
|
84
|
+
body.replaceChildren();
|
|
85
|
+
let section = null;
|
|
86
|
+
for (const row of rows) {
|
|
87
|
+
if (row.sectionId !== section) {
|
|
88
|
+
section = row.sectionId;
|
|
89
|
+
const title = document.createElement('div');
|
|
90
|
+
title.className = 'flows-title admin-section-title';
|
|
91
|
+
title.textContent = row.sectionTitle;
|
|
92
|
+
body.appendChild(title);
|
|
93
|
+
}
|
|
94
|
+
const line = document.createElement('div');
|
|
95
|
+
line.className = 'admin-row';
|
|
96
|
+
// A button's label is its own text; every other kind gets a label span.
|
|
97
|
+
if (row.control !== 'button') {
|
|
98
|
+
const label = document.createElement('span');
|
|
99
|
+
label.className = 'admin-row-label';
|
|
100
|
+
label.textContent = row.label;
|
|
101
|
+
line.appendChild(label);
|
|
102
|
+
}
|
|
103
|
+
line.appendChild(adminControlFor(row));
|
|
104
|
+
body.appendChild(line);
|
|
105
|
+
// Description, console hint and restart flag stay visible under the row
|
|
106
|
+
// rather than in a tooltip: hover discovery hides what a setting does.
|
|
107
|
+
const notes = [
|
|
108
|
+
row.description,
|
|
109
|
+
row.hint,
|
|
110
|
+
row.requiresRestart === true ? adminStrings.restartRequired : undefined,
|
|
111
|
+
].filter((note) => typeof note === 'string' && note !== '');
|
|
112
|
+
for (const note of notes) {
|
|
113
|
+
const el = document.createElement('div');
|
|
114
|
+
el.className = 'admin-note';
|
|
115
|
+
el.textContent = note;
|
|
116
|
+
body.appendChild(el);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function renderAdminConfirm() {
|
|
121
|
+
const confirmBox = $('admin-confirm');
|
|
122
|
+
const status = $('admin-status');
|
|
123
|
+
const output = $('admin-output');
|
|
124
|
+
confirmBox.hidden = adminConfirm.phase !== 'confirming';
|
|
125
|
+
status.hidden = true;
|
|
126
|
+
output.hidden = true;
|
|
127
|
+
if (adminConfirm.phase === 'confirming') {
|
|
128
|
+
$('admin-confirm-preview').textContent = adminConfirm.preview;
|
|
129
|
+
$('admin-confirm-yes').textContent = adminStrings.confirm;
|
|
130
|
+
$('admin-confirm-no').textContent = adminStrings.cancel;
|
|
131
|
+
}
|
|
132
|
+
else if (adminConfirm.phase === 'applied' || adminConfirm.phase === 'error') {
|
|
133
|
+
status.hidden = false;
|
|
134
|
+
status.dataset.kind = adminConfirm.phase;
|
|
135
|
+
status.textContent = adminConfirm.message;
|
|
136
|
+
if (adminConfirm.phase === 'applied' && adminConfirm.output) {
|
|
137
|
+
output.hidden = false;
|
|
138
|
+
output.textContent = adminConfirm.output.join('\n');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Describe the server and render it. Called when the window loads and after
|
|
143
|
+
* every action that is not waiting for a confirmation. */
|
|
144
|
+
async function refreshAdmin() {
|
|
145
|
+
let reply;
|
|
146
|
+
try {
|
|
147
|
+
reply = await send({ action: 'adminDescribe' });
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Service worker asleep or mid-restart: same empty state as no socket.
|
|
151
|
+
renderAdminEmpty(adminStrings.notConnected);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (!reply) {
|
|
155
|
+
renderAdminEmpty(adminStrings.notConnected);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!reply.ok) {
|
|
159
|
+
renderAdminEmpty(describeAdminError(reply.error, adminStrings));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
adminStrings = adminUi(reply.state);
|
|
163
|
+
document.documentElement.lang = adminStrings.language;
|
|
164
|
+
renderAdminRows(buildAdminRows(reply.state));
|
|
165
|
+
renderAdminConfirm();
|
|
166
|
+
}
|
|
167
|
+
async function runAdminAction(request) {
|
|
168
|
+
if (adminBusy)
|
|
169
|
+
return;
|
|
170
|
+
adminBusy = true;
|
|
171
|
+
const status = $('admin-status');
|
|
172
|
+
status.hidden = false;
|
|
173
|
+
status.dataset.kind = 'busy';
|
|
174
|
+
status.textContent = '…';
|
|
175
|
+
$('admin-confirm').hidden = true;
|
|
176
|
+
$('admin-output').hidden = true;
|
|
177
|
+
let result;
|
|
178
|
+
try {
|
|
179
|
+
const reply = await send({
|
|
180
|
+
action: 'adminAction',
|
|
181
|
+
...request,
|
|
182
|
+
});
|
|
183
|
+
result = reply?.result ?? { ok: false, error: 'not-connected' };
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
result = { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
187
|
+
}
|
|
188
|
+
adminBusy = false;
|
|
189
|
+
adminConfirm = reduceConfirm(adminConfirm, { type: 'result', request, result, ui: adminStrings });
|
|
190
|
+
renderAdminConfirm();
|
|
191
|
+
// A confirming toggle or select already shows the new value; leave it until
|
|
192
|
+
// the human decides. Every other outcome re-describes, so an applied change
|
|
193
|
+
// shows the server's new truth and a failed one snaps the control back.
|
|
194
|
+
if (adminConfirm.phase !== 'confirming')
|
|
195
|
+
await refreshAdmin();
|
|
196
|
+
}
|
|
197
|
+
document.getElementById('admin-confirm-yes')?.addEventListener('click', () => {
|
|
198
|
+
if (adminConfirm.phase !== 'confirming')
|
|
199
|
+
return;
|
|
200
|
+
void runAdminAction(adminConfirm.pendingRequest);
|
|
201
|
+
});
|
|
202
|
+
document.getElementById('admin-confirm-no')?.addEventListener('click', () => {
|
|
203
|
+
adminConfirm = reduceConfirm(adminConfirm, { type: 'cancel' });
|
|
204
|
+
renderAdminConfirm();
|
|
205
|
+
void refreshAdmin();
|
|
206
|
+
});
|
|
207
|
+
refreshAdmin().catch(() => {
|
|
208
|
+
renderAdminEmpty(adminStrings.notConnected);
|
|
209
|
+
});
|
|
210
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -154,6 +154,8 @@ function isRuntimeMessage(msg) {
|
|
|
154
154
|
return true;
|
|
155
155
|
if (m.action === 'openActivityWindow')
|
|
156
156
|
return true;
|
|
157
|
+
if (m.action === 'openAdminWindow')
|
|
158
|
+
return true;
|
|
157
159
|
if (m.action === 'activityQuery')
|
|
158
160
|
return true;
|
|
159
161
|
if (m.action === 'activityPurge')
|
|
@@ -357,6 +359,41 @@ chrome.windows.onRemoved.addListener((closedId) => {
|
|
|
357
359
|
if (closedId === activityWindowId)
|
|
358
360
|
activityWindowId = null;
|
|
359
361
|
});
|
|
362
|
+
/** Chrome window geometry for the Admin panel: a form of labelled rows, so
|
|
363
|
+
* narrower than the Activity table but tall enough for every section. */
|
|
364
|
+
const ADMIN_WINDOW = { width: 560, height: 720 };
|
|
365
|
+
/**
|
|
366
|
+
* Open the Admin window, or focus it if it is already open. Same contract as
|
|
367
|
+
* `openActivityWindow`: a dedicated window rather than a section of the popup,
|
|
368
|
+
* which closes the moment it loses focus and is too narrow for the form.
|
|
369
|
+
*/
|
|
370
|
+
let adminWindowId = null;
|
|
371
|
+
async function openAdminWindow() {
|
|
372
|
+
if (adminWindowId !== null) {
|
|
373
|
+
try {
|
|
374
|
+
const existing = await chrome.windows.get(adminWindowId);
|
|
375
|
+
if (existing.id !== undefined) {
|
|
376
|
+
await chrome.windows.update(existing.id, { focused: true });
|
|
377
|
+
return { ok: true, windowId: existing.id };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
// Closed since we last saw it — fall through and open a fresh one.
|
|
382
|
+
adminWindowId = null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const created = await chrome.windows.create({
|
|
386
|
+
url: chrome.runtime.getURL('admin.html'),
|
|
387
|
+
type: 'popup',
|
|
388
|
+
...ADMIN_WINDOW,
|
|
389
|
+
});
|
|
390
|
+
adminWindowId = created?.id ?? null;
|
|
391
|
+
return { ok: true, windowId: created?.id ?? -1 };
|
|
392
|
+
}
|
|
393
|
+
chrome.windows.onRemoved.addListener((closedId) => {
|
|
394
|
+
if (closedId === adminWindowId)
|
|
395
|
+
adminWindowId = null;
|
|
396
|
+
});
|
|
360
397
|
/**
|
|
361
398
|
* Every `browser.flow` currently running in this service worker, across
|
|
362
399
|
* every connected tab. This is the whole basis of the kill switch: the
|
|
@@ -3468,6 +3505,10 @@ chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
|
|
3468
3505
|
void openActivityWindow().then(sendResponse);
|
|
3469
3506
|
return true;
|
|
3470
3507
|
}
|
|
3508
|
+
if (msg.action === 'openAdminWindow') {
|
|
3509
|
+
void openAdminWindow().then(sendResponse);
|
|
3510
|
+
return true;
|
|
3511
|
+
}
|
|
3471
3512
|
if (msg.action === 'activityQuery') {
|
|
3472
3513
|
void queryActivity(msg.query ?? {}).then(sendResponse);
|
|
3473
3514
|
return true;
|