@ninhache/probe 0.8.0 → 0.10.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 +34 -11
- package/dist/{chunk-Z4F2X5IS.js → chunk-VSQVZK7F.js} +177 -6
- package/dist/cli.js +74 -76
- package/dist/{open-2H7PHDYL.js → open-W2TWG636.js} +661 -47
- package/package.json +1 -1
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module";
|
|
2
2
|
const require = __createRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
|
+
DASHBOARD_HISTORY_LENGTH,
|
|
4
5
|
DASHBOARD_INTERVAL_CHOICES_SECONDS,
|
|
5
6
|
DASHBOARD_TIMEOUT_CHOICES_SECONDS,
|
|
6
7
|
createColors,
|
|
8
|
+
curlCommand,
|
|
9
|
+
expectProblem,
|
|
7
10
|
formatCheckResults,
|
|
11
|
+
hostProblem,
|
|
12
|
+
nameProblem,
|
|
13
|
+
openUrl,
|
|
14
|
+
openerCommand,
|
|
15
|
+
parseExpectList,
|
|
16
|
+
pathProblem,
|
|
17
|
+
portProblem,
|
|
8
18
|
runChecks,
|
|
19
|
+
targetUrl,
|
|
9
20
|
truncate
|
|
10
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-VSQVZK7F.js";
|
|
11
22
|
import {
|
|
12
23
|
__commonJS,
|
|
13
24
|
__export,
|
|
@@ -1768,34 +1779,34 @@ var require_react_reconciler_production = __commonJS({
|
|
|
1768
1779
|
var fiber = workInProgress2.child;
|
|
1769
1780
|
null !== fiber && (fiber.return = workInProgress2);
|
|
1770
1781
|
for (; null !== fiber; ) {
|
|
1771
|
-
var
|
|
1772
|
-
if (null !==
|
|
1782
|
+
var list2 = fiber.dependencies;
|
|
1783
|
+
if (null !== list2) {
|
|
1773
1784
|
var nextFiber = fiber.child;
|
|
1774
|
-
|
|
1775
|
-
a: for (; null !==
|
|
1776
|
-
var dependency =
|
|
1777
|
-
|
|
1785
|
+
list2 = list2.firstContext;
|
|
1786
|
+
a: for (; null !== list2; ) {
|
|
1787
|
+
var dependency = list2;
|
|
1788
|
+
list2 = fiber;
|
|
1778
1789
|
for (var i = 0; i < contexts.length; i++)
|
|
1779
1790
|
if (dependency.context === contexts[i]) {
|
|
1780
|
-
|
|
1781
|
-
dependency =
|
|
1791
|
+
list2.lanes |= renderLanes2;
|
|
1792
|
+
dependency = list2.alternate;
|
|
1782
1793
|
null !== dependency && (dependency.lanes |= renderLanes2);
|
|
1783
1794
|
scheduleContextWorkOnParentPath(
|
|
1784
|
-
|
|
1795
|
+
list2.return,
|
|
1785
1796
|
renderLanes2,
|
|
1786
1797
|
workInProgress2
|
|
1787
1798
|
);
|
|
1788
1799
|
forcePropagateEntireTree || (nextFiber = null);
|
|
1789
1800
|
break a;
|
|
1790
1801
|
}
|
|
1791
|
-
|
|
1802
|
+
list2 = dependency.next;
|
|
1792
1803
|
}
|
|
1793
1804
|
} else if (18 === fiber.tag) {
|
|
1794
1805
|
nextFiber = fiber.return;
|
|
1795
1806
|
if (null === nextFiber) throw Error(formatProdErrorMessage(341));
|
|
1796
1807
|
nextFiber.lanes |= renderLanes2;
|
|
1797
|
-
|
|
1798
|
-
null !==
|
|
1808
|
+
list2 = nextFiber.alternate;
|
|
1809
|
+
null !== list2 && (list2.lanes |= renderLanes2);
|
|
1799
1810
|
scheduleContextWorkOnParentPath(nextFiber, renderLanes2, workInProgress2);
|
|
1800
1811
|
nextFiber = null;
|
|
1801
1812
|
} else
|
|
@@ -18586,7 +18597,7 @@ var Box_default = Box;
|
|
|
18586
18597
|
|
|
18587
18598
|
// node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.1/node_modules/ink/build/components/Text.js
|
|
18588
18599
|
var import_react12 = __toESM(require_react(), 1);
|
|
18589
|
-
function Text({ color, backgroundColor, dimColor = false, bold = false, italic = false, underline = false, strikethrough = false, inverse = false, wrap = "wrap", children, "aria-label": ariaLabel, "aria-hidden": ariaHidden = false }) {
|
|
18600
|
+
function Text({ color, backgroundColor, dimColor = false, bold = false, italic = false, underline = false, strikethrough = false, inverse = false, wrap: wrap2 = "wrap", children, "aria-label": ariaLabel, "aria-hidden": ariaHidden = false }) {
|
|
18590
18601
|
const { isScreenReaderEnabled } = (0, import_react12.use)(AccessibilityContext);
|
|
18591
18602
|
const inheritedBackgroundColor = (0, import_react12.use)(BackgroundContext);
|
|
18592
18603
|
const effectiveBackgroundColor = backgroundColor ?? inheritedBackgroundColor;
|
|
@@ -18624,7 +18635,7 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
18624
18635
|
flexGrow: 0,
|
|
18625
18636
|
flexShrink: 1,
|
|
18626
18637
|
flexDirection: "row",
|
|
18627
|
-
textWrap:
|
|
18638
|
+
textWrap: wrap2
|
|
18628
18639
|
}, internal_transform: isScreenReaderEnabled ? void 0 : transform }, childrenOrAriaLabel)
|
|
18629
18640
|
);
|
|
18630
18641
|
}
|
|
@@ -20243,11 +20254,368 @@ var use_window_size_default = useWindowSize;
|
|
|
20243
20254
|
// node_modules/.pnpm/ink@8.0.0_@types+react@19.3.0_react@19.3.1/node_modules/ink/build/hooks/use-box-metrics.js
|
|
20244
20255
|
var import_react35 = __toESM(require_react(), 1);
|
|
20245
20256
|
|
|
20257
|
+
// src/clipboard.ts
|
|
20258
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
20259
|
+
var WL_COPY = ["wl-copy"];
|
|
20260
|
+
var XCLIP = ["xclip", "-selection", "clipboard"];
|
|
20261
|
+
function clipboardTools(platform2, env3) {
|
|
20262
|
+
if (platform2 === "darwin") return [["pbcopy"]];
|
|
20263
|
+
return env3.WAYLAND_DISPLAY ? [WL_COPY, XCLIP] : [XCLIP, WL_COPY];
|
|
20264
|
+
}
|
|
20265
|
+
function copiedWith([command, ...args], text, spawn) {
|
|
20266
|
+
return new Promise((resolve) => {
|
|
20267
|
+
const child = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
20268
|
+
child.once("error", () => resolve(false));
|
|
20269
|
+
child.once("exit", (code) => resolve(code === 0));
|
|
20270
|
+
child.stdin?.on("error", () => {
|
|
20271
|
+
});
|
|
20272
|
+
child.stdin?.end(text);
|
|
20273
|
+
});
|
|
20274
|
+
}
|
|
20275
|
+
async function copyText(text, platform2 = process.platform, env3 = process.env, spawn = nodeSpawn) {
|
|
20276
|
+
for (const tool of clipboardTools(platform2, env3)) {
|
|
20277
|
+
if (await copiedWith(tool, text, spawn)) return true;
|
|
20278
|
+
}
|
|
20279
|
+
return false;
|
|
20280
|
+
}
|
|
20281
|
+
|
|
20282
|
+
// src/dashboard/actions.ts
|
|
20283
|
+
var system = () => ({
|
|
20284
|
+
platform: process.platform,
|
|
20285
|
+
env: process.env,
|
|
20286
|
+
openUrl,
|
|
20287
|
+
copyText
|
|
20288
|
+
});
|
|
20289
|
+
function serviceActions(config, deps = system()) {
|
|
20290
|
+
const service = (name) => config().services[name];
|
|
20291
|
+
const gone = (name) => `${name} is no longer in the config`;
|
|
20292
|
+
return {
|
|
20293
|
+
async onOpen(name) {
|
|
20294
|
+
const found = service(name);
|
|
20295
|
+
if (!found) return gone(name);
|
|
20296
|
+
const url = targetUrl(found);
|
|
20297
|
+
if (await deps.openUrl(url)) return `Opened ${url}`;
|
|
20298
|
+
return `${openerCommand(deps.platform)} not found. The URL:
|
|
20299
|
+
${url}`;
|
|
20300
|
+
},
|
|
20301
|
+
async onCopy(name, timeout) {
|
|
20302
|
+
const found = service(name);
|
|
20303
|
+
if (!found) return gone(name);
|
|
20304
|
+
const command = curlCommand({ ...found, timeout });
|
|
20305
|
+
if (await deps.copyText(command)) return `Copied: ${command}`;
|
|
20306
|
+
const tools = clipboardTools(deps.platform, deps.env).map(([tool]) => tool);
|
|
20307
|
+
return `No ${tools.join(" or ")} to copy with. The command:
|
|
20308
|
+
${command}`;
|
|
20309
|
+
}
|
|
20310
|
+
};
|
|
20311
|
+
}
|
|
20312
|
+
|
|
20246
20313
|
// src/dashboard/Dashboard.tsx
|
|
20247
20314
|
var import_react36 = __toESM(require_react(), 1);
|
|
20248
20315
|
|
|
20316
|
+
// src/dashboard/detail.ts
|
|
20317
|
+
var BLOCKS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
20318
|
+
var HEADER_LINES = 6;
|
|
20319
|
+
var BODY_LINES = 5;
|
|
20320
|
+
var HINT_LINES = 3;
|
|
20321
|
+
var TRANSPORT_HEADERS = /* @__PURE__ */ new Set([
|
|
20322
|
+
"connection",
|
|
20323
|
+
"content-length",
|
|
20324
|
+
"date",
|
|
20325
|
+
"keep-alive",
|
|
20326
|
+
"transfer-encoding"
|
|
20327
|
+
]);
|
|
20328
|
+
function recordLatencies(history, results) {
|
|
20329
|
+
return Object.fromEntries(
|
|
20330
|
+
results.map(({ name, status, skipped, latencyMs }) => {
|
|
20331
|
+
const before = history[name] ?? [];
|
|
20332
|
+
const answered = status !== void 0 && !skipped;
|
|
20333
|
+
return [name, answered ? [...before, latencyMs].slice(-DASHBOARD_HISTORY_LENGTH) : before];
|
|
20334
|
+
})
|
|
20335
|
+
);
|
|
20336
|
+
}
|
|
20337
|
+
function sparkline(values, unicode, width) {
|
|
20338
|
+
if (values.length === 0) return "";
|
|
20339
|
+
const low = Math.min(...values);
|
|
20340
|
+
const high = Math.max(...values);
|
|
20341
|
+
const range = low === high ? `${low}ms` : `${low}${unicode ? "\u2013" : "-"}${high}ms`;
|
|
20342
|
+
if (!unicode) return `${range} over ${values.length} ${values.length === 1 ? "check" : "checks"}`;
|
|
20343
|
+
const room = width - range.length - 1;
|
|
20344
|
+
if (room < 1) return range;
|
|
20345
|
+
const top = BLOCKS.length - 1;
|
|
20346
|
+
const bars = values.slice(-room).map((value) => BLOCKS[high === low ? 0 : Math.round((value - low) / (high - low) * top)]).join("");
|
|
20347
|
+
return `${bars} ${range}`;
|
|
20348
|
+
}
|
|
20349
|
+
function wrap(text, width, maxLines) {
|
|
20350
|
+
const lines = [];
|
|
20351
|
+
let line = "";
|
|
20352
|
+
for (const word of text.split(" ")) {
|
|
20353
|
+
if (line && line.length + 1 + word.length > width) {
|
|
20354
|
+
lines.push(line);
|
|
20355
|
+
line = word;
|
|
20356
|
+
} else line = line ? `${line} ${word}` : word;
|
|
20357
|
+
}
|
|
20358
|
+
lines.push(line);
|
|
20359
|
+
const kept = lines.slice(0, maxLines).map((kept2) => truncate(kept2, width));
|
|
20360
|
+
if (lines.length > maxLines) kept[maxLines - 1] = truncate(`${kept[maxLines - 1]} \u2026`, width);
|
|
20361
|
+
return kept;
|
|
20362
|
+
}
|
|
20363
|
+
function detailLines(result, history, { unicode, width, room }) {
|
|
20364
|
+
const dot = unicode ? "\xB7" : "-";
|
|
20365
|
+
const more = unicode ? "\u2026" : "...";
|
|
20366
|
+
const cut = (text) => truncate(text, width);
|
|
20367
|
+
if (result.skipped) return [{ id: "status", text: cut("skipped: not checked"), tone: "dim" }];
|
|
20368
|
+
const { response, status } = result;
|
|
20369
|
+
const tone = result.ok ? "good" : "bad";
|
|
20370
|
+
const lines = [];
|
|
20371
|
+
if (status === void 0) lines.push({ id: "status", text: cut(result.error ?? "down"), tone });
|
|
20372
|
+
else {
|
|
20373
|
+
const answer = `${status} ${response?.statusText ?? ""}`.trim();
|
|
20374
|
+
lines.push({ id: "status", text: cut(`${answer} ${dot} ${result.latencyMs}ms`), tone });
|
|
20375
|
+
if (result.error) lines.push({ id: "error", text: cut(result.error), tone });
|
|
20376
|
+
}
|
|
20377
|
+
if (result.hint) {
|
|
20378
|
+
for (const [index, text] of wrap(result.hint, width, HINT_LINES).entries())
|
|
20379
|
+
lines.push({ id: `hint ${index}`, text, tone: "dim" });
|
|
20380
|
+
}
|
|
20381
|
+
const latencies = sparkline(history, unicode, width);
|
|
20382
|
+
if (latencies) lines.push({ id: "latencies", text: cut(latencies), tone: "dim" });
|
|
20383
|
+
if (!response) return lines;
|
|
20384
|
+
const transport = ([name]) => TRANSPORT_HEADERS.has(name.toLowerCase());
|
|
20385
|
+
const headers = [
|
|
20386
|
+
...response.headers.filter((header) => !transport(header)),
|
|
20387
|
+
...response.headers.filter(transport)
|
|
20388
|
+
].map(([name, value]) => `${name}: ${value}`);
|
|
20389
|
+
const body = response.bodyNote ? [response.bodyNote] : response.body?.split("\n") ?? [];
|
|
20390
|
+
let headerRoom = Math.min(headers.length, HEADER_LINES);
|
|
20391
|
+
let bodyRoom = Math.min(body.length, BODY_LINES);
|
|
20392
|
+
const left = Math.max(0, room - lines.length);
|
|
20393
|
+
while (headerRoom + bodyRoom > left) {
|
|
20394
|
+
if (bodyRoom > 1 && bodyRoom >= headerRoom) bodyRoom -= 1;
|
|
20395
|
+
else if (headerRoom > 0) headerRoom -= 1;
|
|
20396
|
+
else bodyRoom -= 1;
|
|
20397
|
+
}
|
|
20398
|
+
const hiddenHeaders = headers.length - headerRoom;
|
|
20399
|
+
for (const [index, header] of headers.slice(0, headerRoom).entries()) {
|
|
20400
|
+
const last = index === headerRoom - 1 && hiddenHeaders > 0;
|
|
20401
|
+
lines.push(
|
|
20402
|
+
last ? {
|
|
20403
|
+
id: `header ${index}`,
|
|
20404
|
+
text: cut(`${more} ${hiddenHeaders + 1} more headers`),
|
|
20405
|
+
tone: "dim"
|
|
20406
|
+
} : { id: `header ${index}`, text: cut(header), tone: "dim" }
|
|
20407
|
+
);
|
|
20408
|
+
}
|
|
20409
|
+
const cutBody = body.length > bodyRoom || response.bodyCut;
|
|
20410
|
+
for (const [index, text] of body.slice(0, bodyRoom).entries()) {
|
|
20411
|
+
const last = index === bodyRoom - 1 && cutBody;
|
|
20412
|
+
lines.push({
|
|
20413
|
+
id: `body ${index}`,
|
|
20414
|
+
text: last ? `${truncate(text, Math.max(1, width - more.length - 1))} ${more}`.trim() : cut(text),
|
|
20415
|
+
tone: response.bodyNote ? "dim" : void 0
|
|
20416
|
+
});
|
|
20417
|
+
}
|
|
20418
|
+
return lines;
|
|
20419
|
+
}
|
|
20420
|
+
|
|
20421
|
+
// src/dashboard/form.ts
|
|
20422
|
+
var ROWS = [
|
|
20423
|
+
{ key: "name", label: "Name", placeholder: "api" },
|
|
20424
|
+
{ key: "port", label: "Port", placeholder: "3000" },
|
|
20425
|
+
{ key: "host", label: "Host", placeholder: "localhost" },
|
|
20426
|
+
{ key: "path", label: "Path", placeholder: "/" },
|
|
20427
|
+
{ key: "expect", label: "Expect", placeholder: "any 2xx" }
|
|
20428
|
+
];
|
|
20429
|
+
var EMPTY_FORM = { name: "", port: "", host: "", path: "", expect: "" };
|
|
20430
|
+
function formRows(adding) {
|
|
20431
|
+
return adding ? ROWS : ROWS.filter(({ key }) => key !== "name");
|
|
20432
|
+
}
|
|
20433
|
+
function formValues({ host, port, path, expect }) {
|
|
20434
|
+
return { name: "", port: String(port), host, path, expect: [expect ?? []].flat().join(",") };
|
|
20435
|
+
}
|
|
20436
|
+
function formFields(values) {
|
|
20437
|
+
return {
|
|
20438
|
+
port: Number(values.port),
|
|
20439
|
+
host: values.host || "localhost",
|
|
20440
|
+
path: values.path || "/",
|
|
20441
|
+
expect: values.expect ? parseExpectList(values.expect) : void 0
|
|
20442
|
+
};
|
|
20443
|
+
}
|
|
20444
|
+
function formProblem(rows, values, taken) {
|
|
20445
|
+
const problems = {
|
|
20446
|
+
name: () => nameProblem(values.name, taken),
|
|
20447
|
+
port: () => portProblem(values.port),
|
|
20448
|
+
host: () => values.host ? hostProblem(values.host) : void 0,
|
|
20449
|
+
path: () => values.path ? pathProblem(values.path) : void 0,
|
|
20450
|
+
expect: () => values.expect ? expectProblem(values.expect) : void 0
|
|
20451
|
+
};
|
|
20452
|
+
for (const [row, { key }] of rows.entries()) {
|
|
20453
|
+
const message = problems[key]();
|
|
20454
|
+
if (message) return { row, message };
|
|
20455
|
+
}
|
|
20456
|
+
return void 0;
|
|
20457
|
+
}
|
|
20458
|
+
|
|
20459
|
+
// src/dashboard/keymap.ts
|
|
20460
|
+
var list = (binding) => ({
|
|
20461
|
+
screens: ["list"],
|
|
20462
|
+
section: "List",
|
|
20463
|
+
...binding
|
|
20464
|
+
});
|
|
20465
|
+
var BINDINGS = [
|
|
20466
|
+
list({ keys: "\u2191\u2193 jk", ascii: "jk", action: "move", help: "Move", drop: 2 }),
|
|
20467
|
+
list({ keys: "r", action: "check", help: "Check now", barless: true, command: "check" }),
|
|
20468
|
+
list({
|
|
20469
|
+
keys: "\u21B5",
|
|
20470
|
+
ascii: "enter",
|
|
20471
|
+
action: "service",
|
|
20472
|
+
help: "Service detail",
|
|
20473
|
+
drop: 5,
|
|
20474
|
+
command: "detail"
|
|
20475
|
+
}),
|
|
20476
|
+
list({ keys: "u", action: "open", help: "Open its URL", barless: true, command: "open" }),
|
|
20477
|
+
list({ keys: "c", action: "copy", help: "Copy curl command", barless: true, command: "copy" }),
|
|
20478
|
+
list({ keys: "e", action: "edit", help: "Edit the service", barless: true, command: "edit" }),
|
|
20479
|
+
list({ keys: "d", action: "remove", help: "Remove it", barless: true, command: "remove" }),
|
|
20480
|
+
list({ keys: "a", action: "add", help: "Add a service", drop: 3, command: "add" }),
|
|
20481
|
+
list({ keys: "/", action: "filter", help: "Filter by name", drop: 4, command: "filter" }),
|
|
20482
|
+
list({ keys: ":", action: "commands", help: "Find a command", drop: 1 }),
|
|
20483
|
+
list({ keys: "o", action: "options", help: "Options", barless: true, command: "options" }),
|
|
20484
|
+
{
|
|
20485
|
+
screens: ["panel"],
|
|
20486
|
+
section: "Panels",
|
|
20487
|
+
keys: "\u2191\u2193 jk",
|
|
20488
|
+
ascii: "jk",
|
|
20489
|
+
action: "move",
|
|
20490
|
+
help: "Move",
|
|
20491
|
+
drop: 1
|
|
20492
|
+
},
|
|
20493
|
+
{
|
|
20494
|
+
screens: ["panel"],
|
|
20495
|
+
section: "Panels",
|
|
20496
|
+
keys: "\u2190\u2192 hl",
|
|
20497
|
+
ascii: "hl",
|
|
20498
|
+
action: "change",
|
|
20499
|
+
help: "Change the value",
|
|
20500
|
+
drop: 2
|
|
20501
|
+
},
|
|
20502
|
+
{ screens: ["panel"], section: "Panels", keys: "esc", action: "close", help: "Close" },
|
|
20503
|
+
{
|
|
20504
|
+
screens: ["filter"],
|
|
20505
|
+
section: "Filter",
|
|
20506
|
+
keys: "\u2191\u2193",
|
|
20507
|
+
ascii: "arrows",
|
|
20508
|
+
action: "move",
|
|
20509
|
+
help: "Move",
|
|
20510
|
+
drop: 1
|
|
20511
|
+
},
|
|
20512
|
+
{
|
|
20513
|
+
screens: ["filter"],
|
|
20514
|
+
section: "Filter",
|
|
20515
|
+
keys: "\u21B5",
|
|
20516
|
+
ascii: "enter",
|
|
20517
|
+
action: "keep",
|
|
20518
|
+
help: "Keep the filter"
|
|
20519
|
+
},
|
|
20520
|
+
{ screens: ["filter"], section: "Filter", keys: "esc", action: "clear", help: "Clear it" },
|
|
20521
|
+
{ screens: ["palette"], keys: "\u2191\u2193", ascii: "arrows", action: "move", drop: 1 },
|
|
20522
|
+
{ screens: ["palette"], keys: "\u21B5", ascii: "enter", action: "run" },
|
|
20523
|
+
{ screens: ["palette"], keys: "esc", action: "close" },
|
|
20524
|
+
{
|
|
20525
|
+
screens: ["form"],
|
|
20526
|
+
section: "Add and edit",
|
|
20527
|
+
keys: "\u2191\u2193 tab",
|
|
20528
|
+
ascii: "tab",
|
|
20529
|
+
action: "field",
|
|
20530
|
+
help: "Change field",
|
|
20531
|
+
drop: 1
|
|
20532
|
+
},
|
|
20533
|
+
{
|
|
20534
|
+
screens: ["form"],
|
|
20535
|
+
section: "Add and edit",
|
|
20536
|
+
keys: "\u21B5",
|
|
20537
|
+
ascii: "enter",
|
|
20538
|
+
action: "save",
|
|
20539
|
+
help: "Save"
|
|
20540
|
+
},
|
|
20541
|
+
{ screens: ["form"], section: "Add and edit", keys: "esc", action: "cancel", help: "Cancel" },
|
|
20542
|
+
{ screens: ["confirm"], keys: "y", action: "remove" },
|
|
20543
|
+
{ screens: ["confirm"], keys: "n esc", action: "keep" },
|
|
20544
|
+
{ screens: ["help"], keys: "? esc", action: "close" },
|
|
20545
|
+
{
|
|
20546
|
+
screens: ["list", "panel"],
|
|
20547
|
+
section: "Outside a text field",
|
|
20548
|
+
keys: "?",
|
|
20549
|
+
action: "help",
|
|
20550
|
+
help: "This help",
|
|
20551
|
+
command: "help"
|
|
20552
|
+
},
|
|
20553
|
+
{
|
|
20554
|
+
screens: ["list", "panel", "help"],
|
|
20555
|
+
section: "Outside a text field",
|
|
20556
|
+
keys: "q",
|
|
20557
|
+
action: "quit",
|
|
20558
|
+
help: "Quit",
|
|
20559
|
+
command: "quit"
|
|
20560
|
+
}
|
|
20561
|
+
];
|
|
20562
|
+
var SECTIONS = [
|
|
20563
|
+
"List",
|
|
20564
|
+
"Panels",
|
|
20565
|
+
"Filter",
|
|
20566
|
+
"Add and edit",
|
|
20567
|
+
"Outside a text field"
|
|
20568
|
+
];
|
|
20569
|
+
var written = (binding, unicode) => unicode ? binding.keys : binding.ascii ?? binding.keys;
|
|
20570
|
+
function helpBar(screen, unicode, columns) {
|
|
20571
|
+
const shown = BINDINGS.filter(({ screens, barless }) => screens.includes(screen) && !barless);
|
|
20572
|
+
const line = () => shown.map((binding) => `${written(binding, unicode)} ${binding.action}`).join(unicode ? " \xB7 " : " - ");
|
|
20573
|
+
while (line().length > columns) {
|
|
20574
|
+
const droppable = shown.filter(({ drop }) => drop !== void 0);
|
|
20575
|
+
if (droppable.length === 0) break;
|
|
20576
|
+
const first = droppable.reduce((a, b) => a.drop <= b.drop ? a : b);
|
|
20577
|
+
shown.splice(shown.indexOf(first), 1);
|
|
20578
|
+
}
|
|
20579
|
+
return line();
|
|
20580
|
+
}
|
|
20581
|
+
function keyCommand(input, enter) {
|
|
20582
|
+
return BINDINGS.find(({ keys, command }) => command && (enter ? keys === "\u21B5" : keys === input))?.command;
|
|
20583
|
+
}
|
|
20584
|
+
function paletteEntries(unicode, query = "") {
|
|
20585
|
+
const wanted = query.trim().toLowerCase();
|
|
20586
|
+
return BINDINGS.flatMap((binding) => {
|
|
20587
|
+
const { command, help, action } = binding;
|
|
20588
|
+
if (!command || !help || !`${help} ${action}`.toLowerCase().includes(wanted)) return [];
|
|
20589
|
+
return [{ command, label: help, keys: written(binding, unicode) }];
|
|
20590
|
+
});
|
|
20591
|
+
}
|
|
20592
|
+
var HELP_GAP = 3;
|
|
20593
|
+
function sectionLines(sections, unicode) {
|
|
20594
|
+
const described = BINDINGS.filter(({ help }) => help !== void 0);
|
|
20595
|
+
const width = Math.max(...described.map((binding) => written(binding, unicode).length));
|
|
20596
|
+
return sections.flatMap((title) => [
|
|
20597
|
+
{ id: title, text: title, heading: true },
|
|
20598
|
+
...described.filter(({ section }) => section === title).map((binding) => ({
|
|
20599
|
+
id: `${title} ${binding.keys}`,
|
|
20600
|
+
text: ` ${written(binding, unicode).padEnd(width)} ${binding.help}`,
|
|
20601
|
+
heading: false
|
|
20602
|
+
}))
|
|
20603
|
+
]);
|
|
20604
|
+
}
|
|
20605
|
+
function helpLines(unicode) {
|
|
20606
|
+
return sectionLines(SECTIONS, unicode);
|
|
20607
|
+
}
|
|
20608
|
+
function helpColumns(unicode, columns) {
|
|
20609
|
+
const [first, ...others] = SECTIONS;
|
|
20610
|
+
const sides = [sectionLines([first], unicode), sectionLines(others, unicode)];
|
|
20611
|
+
const widths = sides.map((side) => Math.max(...side.map(({ text }) => text.length)));
|
|
20612
|
+
const fits = widths[0] + HELP_GAP + widths[1] <= columns;
|
|
20613
|
+
return fits ? sides : [helpLines(unicode)];
|
|
20614
|
+
}
|
|
20615
|
+
|
|
20249
20616
|
// src/dashboard/keys.ts
|
|
20250
20617
|
var ESCAPE2 = String.fromCharCode(27);
|
|
20618
|
+
var DELETE = String.fromCharCode(127);
|
|
20251
20619
|
var ARROWS = { A: "up", B: "down", C: "right", D: "left" };
|
|
20252
20620
|
var KEY_PATTERN = new RegExp(`${ESCAPE2}\\[([ABCD])|[\\s\\S]`, "g");
|
|
20253
20621
|
function named(key) {
|
|
@@ -20257,8 +20625,13 @@ function named(key) {
|
|
|
20257
20625
|
if (key.rightArrow) return "right";
|
|
20258
20626
|
if (key.return) return "return";
|
|
20259
20627
|
if (key.escape) return "escape";
|
|
20628
|
+
if (key.backspace || key.delete) return "backspace";
|
|
20629
|
+
if (key.tab) return "tab";
|
|
20260
20630
|
return void 0;
|
|
20261
20631
|
}
|
|
20632
|
+
function typedCharacter({ input, key }) {
|
|
20633
|
+
return key === void 0 && input.length === 1 && input >= " " && input !== DELETE ? input : void 0;
|
|
20634
|
+
}
|
|
20262
20635
|
function splitKeys(input, key) {
|
|
20263
20636
|
const name = named(key);
|
|
20264
20637
|
if (name || input.length <= 1) return [{ input, key: name }];
|
|
@@ -20266,6 +20639,8 @@ function splitKeys(input, key) {
|
|
|
20266
20639
|
if (arrow) return { input: "", key: ARROWS[arrow] };
|
|
20267
20640
|
if (text === "\r" || text === "\n") return { input: "", key: "return" };
|
|
20268
20641
|
if (text === ESCAPE2) return { input: "", key: "escape" };
|
|
20642
|
+
if (text === DELETE || text === "\b") return { input: "", key: "backspace" };
|
|
20643
|
+
if (text === " ") return { input: "", key: "tab" };
|
|
20269
20644
|
return { input: text };
|
|
20270
20645
|
});
|
|
20271
20646
|
}
|
|
@@ -20294,8 +20669,17 @@ var TIMEOUT_ROW = SERVICE_ROWS.indexOf("Timeout");
|
|
|
20294
20669
|
var NO_TIMEOUTS = {};
|
|
20295
20670
|
var MARKER_WIDTH = 2;
|
|
20296
20671
|
var PANEL_WIDTH = 30;
|
|
20672
|
+
var DETAIL_WIDTH = 60;
|
|
20673
|
+
var DEFAULT_ROWS = 24;
|
|
20674
|
+
var FORM_WIDTH = 48;
|
|
20675
|
+
var CURSOR = "_";
|
|
20297
20676
|
var PANEL_FRAME_WIDTH = 4;
|
|
20298
20677
|
var seconds = (ms) => `${ms / 1e3}s`;
|
|
20678
|
+
function ending(text, max) {
|
|
20679
|
+
const characters = Array.from(text);
|
|
20680
|
+
if (characters.length <= max) return text;
|
|
20681
|
+
return max <= 1 ? "\u2026" : `\u2026${characters.slice(characters.length - max + 1).join("")}`;
|
|
20682
|
+
}
|
|
20299
20683
|
function Dashboard({
|
|
20300
20684
|
terminal,
|
|
20301
20685
|
settings: initial,
|
|
@@ -20304,17 +20688,29 @@ function Dashboard({
|
|
|
20304
20688
|
check: check2,
|
|
20305
20689
|
onSettingsChange,
|
|
20306
20690
|
onSkipChange,
|
|
20307
|
-
onTimeoutChange
|
|
20691
|
+
onTimeoutChange,
|
|
20692
|
+
onOpen,
|
|
20693
|
+
onCopy,
|
|
20694
|
+
serviceFields,
|
|
20695
|
+
onServiceWrite
|
|
20308
20696
|
}) {
|
|
20309
20697
|
const { exit } = useApp();
|
|
20310
|
-
const { columns } = use_window_size_default();
|
|
20698
|
+
const { columns, rows: windowRows } = use_window_size_default();
|
|
20311
20699
|
const [latest, setLatest] = (0, import_react36.useState)(null);
|
|
20700
|
+
const [history, setHistory] = (0, import_react36.useState)({});
|
|
20312
20701
|
const [selected, setSelected] = (0, import_react36.useState)(0);
|
|
20313
20702
|
const [settings, setSettings] = (0, import_react36.useState)(initial);
|
|
20314
20703
|
const [skipped, setSkipped] = (0, import_react36.useState)(() => new Set(initiallySkipped));
|
|
20315
20704
|
const [timeouts, setTimeouts] = (0, import_react36.useState)(initialTimeouts);
|
|
20316
20705
|
const [panel, setPanel] = (0, import_react36.useState)(null);
|
|
20706
|
+
const [dialog, setDialog] = (0, import_react36.useState)(null);
|
|
20707
|
+
const [filter, setFilter] = (0, import_react36.useState)("");
|
|
20708
|
+
const [filtering, setFiltering] = (0, import_react36.useState)(false);
|
|
20709
|
+
const [palette, setPalette] = (0, import_react36.useState)(null);
|
|
20710
|
+
const [writes, setWrites] = (0, import_react36.useState)(0);
|
|
20711
|
+
const [help, setHelp] = (0, import_react36.useState)(false);
|
|
20317
20712
|
const [unsaved, setUnsaved] = (0, import_react36.useState)();
|
|
20713
|
+
const [notice, setNotice] = (0, import_react36.useState)();
|
|
20318
20714
|
const skipPause = (0, import_react36.useRef)(() => {
|
|
20319
20715
|
});
|
|
20320
20716
|
const track = (saved) => {
|
|
@@ -20329,6 +20725,7 @@ function Dashboard({
|
|
|
20329
20725
|
const next = await check2(signal, timeoutMs, skipped, timeouts);
|
|
20330
20726
|
if (signal.aborted) return;
|
|
20331
20727
|
setLatest(next);
|
|
20728
|
+
setHistory((before) => recordLatencies(before, next));
|
|
20332
20729
|
await new Promise((resolve) => {
|
|
20333
20730
|
const timer = setTimeout(resolve, intervalMs);
|
|
20334
20731
|
skipPause.current = () => {
|
|
@@ -20341,9 +20738,59 @@ function Dashboard({
|
|
|
20341
20738
|
};
|
|
20342
20739
|
void loop();
|
|
20343
20740
|
return () => controller.abort();
|
|
20344
|
-
}, [check2, intervalMs, timeoutMs, skipped, timeouts]);
|
|
20345
|
-
const
|
|
20741
|
+
}, [check2, intervalMs, timeoutMs, skipped, timeouts, writes]);
|
|
20742
|
+
const wanted = filter.toLowerCase();
|
|
20743
|
+
const shown = (latest ?? []).filter(({ name }) => name.toLowerCase().includes(wanted));
|
|
20744
|
+
const last = Math.max(0, shown.length - 1);
|
|
20346
20745
|
const current = Math.min(selected, last);
|
|
20746
|
+
const write = async (change2, done) => {
|
|
20747
|
+
const outcome = await onServiceWrite?.(change2);
|
|
20748
|
+
if (!outcome) return;
|
|
20749
|
+
setUnsaved(outcome.failure);
|
|
20750
|
+
if (!outcome.saved) return;
|
|
20751
|
+
setNotice(outcome.warning ? `${done}
|
|
20752
|
+
${outcome.warning}` : done);
|
|
20753
|
+
if (change2.kind === "remove") {
|
|
20754
|
+
setLatest((results2) => results2?.filter(({ name }) => name !== change2.name) ?? null);
|
|
20755
|
+
setTimeouts(({ [change2.name]: _, ...others }) => others);
|
|
20756
|
+
}
|
|
20757
|
+
setWrites((count) => count + 1);
|
|
20758
|
+
};
|
|
20759
|
+
const typeInForm = (form, keystroke) => {
|
|
20760
|
+
const { key } = keystroke;
|
|
20761
|
+
const rows2 = formRows(form.editing === void 0);
|
|
20762
|
+
const field = rows2[form.row]?.key;
|
|
20763
|
+
const typed = typedCharacter(keystroke);
|
|
20764
|
+
const edit = (value) => field && setDialog({ ...form, values: { ...form.values, [field]: value }, problem: void 0 });
|
|
20765
|
+
if (key === "escape") setDialog(null);
|
|
20766
|
+
else if (key === "up") setDialog({ ...form, row: Math.max(0, form.row - 1) });
|
|
20767
|
+
else if (key === "down") setDialog({ ...form, row: Math.min(rows2.length - 1, form.row + 1) });
|
|
20768
|
+
else if (key === "tab") setDialog({ ...form, row: (form.row + 1) % rows2.length });
|
|
20769
|
+
else if (key === "backspace" && field) edit(form.values[field].slice(0, -1));
|
|
20770
|
+
else if (typed && field) edit(form.values[field] + typed);
|
|
20771
|
+
else if (key === "return") {
|
|
20772
|
+
const taken2 = (latest ?? []).map(({ name }) => name);
|
|
20773
|
+
const problem = formProblem(rows2, form.values, taken2);
|
|
20774
|
+
if (problem) return setDialog({ ...form, row: problem.row, problem: problem.message });
|
|
20775
|
+
setDialog(null);
|
|
20776
|
+
const fields = formFields(form.values);
|
|
20777
|
+
if (form.editing === void 0)
|
|
20778
|
+
void write({ kind: "add", name: form.values.name, fields }, `Added ${form.values.name}`);
|
|
20779
|
+
else void write({ kind: "edit", name: form.editing, fields }, `Saved ${form.editing}`);
|
|
20780
|
+
}
|
|
20781
|
+
};
|
|
20782
|
+
const typeFilter = (keystroke) => {
|
|
20783
|
+
const { key } = keystroke;
|
|
20784
|
+
const typed = typedCharacter(keystroke);
|
|
20785
|
+
if (key === "escape") {
|
|
20786
|
+
setFilter("");
|
|
20787
|
+
setFiltering(false);
|
|
20788
|
+
} else if (key === "return") setFiltering(false);
|
|
20789
|
+
else if (key === "backspace") setFilter(filter.slice(0, -1));
|
|
20790
|
+
else if (key === "up") setSelected(Math.max(0, current - 1));
|
|
20791
|
+
else if (key === "down") setSelected(Math.min(last, current + 1));
|
|
20792
|
+
else if (typed) setFilter(filter + typed);
|
|
20793
|
+
};
|
|
20347
20794
|
const change = (rowIndex, direction) => {
|
|
20348
20795
|
const row = SETTING_ROWS[rowIndex];
|
|
20349
20796
|
if (!row) return;
|
|
@@ -20366,18 +20813,78 @@ function Dashboard({
|
|
|
20366
20813
|
setTimeouts({ ...timeouts, [name]: value });
|
|
20367
20814
|
track(onTimeoutChange?.(name, value));
|
|
20368
20815
|
};
|
|
20369
|
-
const
|
|
20816
|
+
const run = (command) => {
|
|
20817
|
+
const name = shown[current]?.name;
|
|
20818
|
+
switch (command) {
|
|
20819
|
+
case "check":
|
|
20820
|
+
return skipPause.current();
|
|
20821
|
+
case "options":
|
|
20822
|
+
return setPanel({ kind: "options", row: 0 });
|
|
20823
|
+
case "detail":
|
|
20824
|
+
return name ? setPanel({ kind: "service", name, row: 0 }) : void 0;
|
|
20825
|
+
case "open":
|
|
20826
|
+
return name ? void onOpen?.(name).then(setNotice) : void 0;
|
|
20827
|
+
case "copy":
|
|
20828
|
+
return name ? void onCopy?.(name, timeouts[name]).then(setNotice) : void 0;
|
|
20829
|
+
case "filter":
|
|
20830
|
+
return setFiltering(true);
|
|
20831
|
+
case "add":
|
|
20832
|
+
return onServiceWrite ? setDialog({ kind: "form", row: 0, values: EMPTY_FORM }) : void 0;
|
|
20833
|
+
case "edit": {
|
|
20834
|
+
const fields = onServiceWrite && name ? serviceFields?.(name) : void 0;
|
|
20835
|
+
if (fields) setDialog({ kind: "form", editing: name, row: 0, values: formValues(fields) });
|
|
20836
|
+
return;
|
|
20837
|
+
}
|
|
20838
|
+
case "remove":
|
|
20839
|
+
return onServiceWrite && name ? setDialog({ kind: "confirm", name }) : void 0;
|
|
20840
|
+
case "help":
|
|
20841
|
+
return setHelp(true);
|
|
20842
|
+
case "quit":
|
|
20843
|
+
return exit();
|
|
20844
|
+
default:
|
|
20845
|
+
return command;
|
|
20846
|
+
}
|
|
20847
|
+
};
|
|
20848
|
+
const typePalette = ({ query, row }, keystroke) => {
|
|
20849
|
+
const { key } = keystroke;
|
|
20850
|
+
const typed = typedCharacter(keystroke);
|
|
20851
|
+
const entries = paletteEntries(terminal.unicode, query);
|
|
20852
|
+
if (key === "escape") setPalette(null);
|
|
20853
|
+
else if (key === "return") {
|
|
20854
|
+
const entry = entries[row];
|
|
20855
|
+
if (!entry) return;
|
|
20856
|
+
setPalette(null);
|
|
20857
|
+
run(entry.command);
|
|
20858
|
+
} else if (key === "up") setPalette({ query, row: Math.max(0, row - 1) });
|
|
20859
|
+
else if (key === "down")
|
|
20860
|
+
setPalette({ query, row: Math.max(0, Math.min(entries.length - 1, row + 1)) });
|
|
20861
|
+
else if (key === "backspace") setPalette({ query: query.slice(0, -1), row: 0 });
|
|
20862
|
+
else if (typed) setPalette({ query: query + typed, row: 0 });
|
|
20863
|
+
};
|
|
20864
|
+
const press = (keystroke) => {
|
|
20865
|
+
const { input, key } = keystroke;
|
|
20370
20866
|
const up2 = key === "up" || input === "k";
|
|
20371
20867
|
const down = key === "down" || input === "j";
|
|
20372
20868
|
const left = key === "left" || input === "h";
|
|
20373
20869
|
const right = key === "right" || input === "l";
|
|
20374
20870
|
const enter = key === "return";
|
|
20375
|
-
|
|
20376
|
-
if (
|
|
20871
|
+
setNotice(void 0);
|
|
20872
|
+
if (dialog?.kind === "form") typeInForm(dialog, keystroke);
|
|
20873
|
+
else if (dialog) {
|
|
20874
|
+
setDialog(null);
|
|
20875
|
+
if (input === "y")
|
|
20876
|
+
void write({ kind: "remove", name: dialog.name }, `Removed ${dialog.name}`);
|
|
20877
|
+
} else if (palette) typePalette(palette, keystroke);
|
|
20878
|
+
else if (filtering) typeFilter(keystroke);
|
|
20879
|
+
else if (input === "q") exit();
|
|
20880
|
+
else if (help) {
|
|
20881
|
+
if (input === "?" || key === "escape") setHelp(false);
|
|
20882
|
+
} else if (input === "?") setHelp(true);
|
|
20377
20883
|
else if (panel === null) {
|
|
20378
|
-
|
|
20379
|
-
|
|
20380
|
-
else if (
|
|
20884
|
+
const command = keyCommand(input, enter);
|
|
20885
|
+
if (command) run(command);
|
|
20886
|
+
else if (input === ":") setPalette({ query: "", row: 0 });
|
|
20887
|
+
else if (key === "escape") setFilter("");
|
|
20381
20888
|
else if (up2) setSelected(Math.max(0, current - 1));
|
|
20382
20889
|
else if (down) setSelected(Math.min(last, current + 1));
|
|
20383
20890
|
} else if (key === "escape" || (panel.kind === "options" ? input === "o" : enter))
|
|
@@ -20419,27 +20926,61 @@ function Dashboard({
|
|
|
20419
20926
|
const dot = unicode ? "\xB7" : "-";
|
|
20420
20927
|
const marker = unicode ? "\u203A " : "> ";
|
|
20421
20928
|
const cut = (text) => truncate(text, Math.max(1, columns));
|
|
20929
|
+
if (help) {
|
|
20930
|
+
const [left = [], right = []] = helpColumns(unicode, columns);
|
|
20931
|
+
const leftWidth = Math.max(...left.map(({ text }) => text.length));
|
|
20932
|
+
const paint = (line) => line?.heading ? colors.bold(cut(line.text)) : cut(line?.text ?? "");
|
|
20933
|
+
const lines = Array.from({ length: Math.max(left.length, right.length) }, (_, index) => {
|
|
20934
|
+
const near = left[index];
|
|
20935
|
+
const far = right[index];
|
|
20936
|
+
const gap = " ".repeat(leftWidth - (near?.text.length ?? 0) + HELP_GAP);
|
|
20937
|
+
return {
|
|
20938
|
+
id: near?.id ?? far?.id,
|
|
20939
|
+
text: far ? `${paint(near)}${gap}${paint(far)}` : paint(near)
|
|
20940
|
+
};
|
|
20941
|
+
});
|
|
20942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
20943
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.bold(cut(`probe ${dot} keys`)) }),
|
|
20944
|
+
lines.map(({ id, text }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: text }, id)),
|
|
20945
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim(cut(helpBar("help", unicode, columns))) })
|
|
20946
|
+
] });
|
|
20947
|
+
}
|
|
20422
20948
|
if (!latest) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cut("probe: checking\u2026") });
|
|
20423
|
-
const
|
|
20949
|
+
const asShown = (result) => {
|
|
20424
20950
|
if (result.skipped || !skipped.has(result.name)) return result;
|
|
20425
20951
|
const { name, type, target } = result;
|
|
20426
20952
|
return { name, type, target, ok: true, skipped: true, latencyMs: 0 };
|
|
20427
|
-
}
|
|
20428
|
-
const
|
|
20429
|
-
const
|
|
20430
|
-
const
|
|
20953
|
+
};
|
|
20954
|
+
const results = shown.map(asShown);
|
|
20955
|
+
const all = latest.filter((result) => !result.skipped && !skipped.has(result.name));
|
|
20956
|
+
const up = all.filter((result) => result.ok).length;
|
|
20957
|
+
const aside = latest.length - all.length;
|
|
20431
20958
|
const summary = [
|
|
20432
20959
|
"probe",
|
|
20433
|
-
`${up}/${
|
|
20960
|
+
`${up}/${all.length} up`,
|
|
20434
20961
|
...aside > 0 ? [`${aside} skipped`] : [],
|
|
20435
|
-
`every ${seconds(intervalMs)}
|
|
20962
|
+
`every ${seconds(intervalMs)}`,
|
|
20963
|
+
...filter && !filtering ? [`filter: ${filter}`] : []
|
|
20436
20964
|
].join(` ${dot} `);
|
|
20437
|
-
const rows =
|
|
20438
|
-
|
|
20439
|
-
|
|
20440
|
-
|
|
20965
|
+
const rows = results.length === 0 ? [] : (
|
|
20966
|
+
// One line per service: the hint of the selected one goes under the list.
|
|
20967
|
+
formatCheckResults(
|
|
20968
|
+
results.map(({ hint: _, ...result }) => result),
|
|
20969
|
+
{ ...terminal, columns: Math.max(1, columns - MARKER_WIDTH) }
|
|
20970
|
+
).split("\n")
|
|
20971
|
+
);
|
|
20972
|
+
const empty = filter ? `no service matches "${filter}"` : onServiceWrite ? "no service: press a to add one" : "no service";
|
|
20441
20973
|
const focused = results[current];
|
|
20442
|
-
const panelWidth = Math.min(PANEL_WIDTH, columns);
|
|
20974
|
+
const panelWidth = Math.min(panel?.kind === "service" ? DETAIL_WIDTH : PANEL_WIDTH, columns);
|
|
20975
|
+
const panelRoom = Math.max(1, panelWidth - PANEL_FRAME_WIDTH);
|
|
20976
|
+
const detailed = panel?.kind === "service" ? latest.find(({ name }) => name === panel.name) : void 0;
|
|
20977
|
+
const taken = 1 + Math.max(1, rows.length) + 1 + 2 + 1 + 1 + SERVICE_ROWS.length + 1 + 1;
|
|
20978
|
+
const detail = detailed ? detailLines(asShown(detailed), history[detailed.name] ?? [], {
|
|
20979
|
+
unicode,
|
|
20980
|
+
width: panelRoom,
|
|
20981
|
+
room: (windowRows || DEFAULT_ROWS) - taken - (unsaved === void 0 ? 0 : 1)
|
|
20982
|
+
}) : [];
|
|
20983
|
+
const toned = ({ text, tone }) => tone === "good" ? colors.green(text) : tone === "bad" ? colors.red(text) : tone === "dim" ? colors.dim(text) : text;
|
|
20443
20984
|
const panelRows = panel?.kind === "options" ? SETTING_ROWS.map((row) => [row.label, seconds(settings[row.key])]) : panel ? [
|
|
20444
20985
|
["Skip", skipped.has(panel.name) ? "yes" : "no"],
|
|
20445
20986
|
[
|
|
@@ -20448,16 +20989,29 @@ function Dashboard({
|
|
|
20448
20989
|
]
|
|
20449
20990
|
] : [];
|
|
20450
20991
|
const labelWidth = Math.max(0, ...panelRows.map(([label]) => label.length));
|
|
20451
|
-
const
|
|
20992
|
+
const screen = dialog?.kind === "form" ? "form" : dialog ? "confirm" : palette ? "palette" : filtering ? "filter" : panel ? "panel" : "list";
|
|
20993
|
+
const hint = helpBar(screen, unicode, columns);
|
|
20994
|
+
const commands = palette ? paletteEntries(unicode, palette.query) : [];
|
|
20995
|
+
const commandWidth = Math.max(0, ...commands.map(({ label }) => label.length));
|
|
20996
|
+
const paletteRoom = Math.max(
|
|
20997
|
+
1,
|
|
20998
|
+
(windowRows || DEFAULT_ROWS) - (1 + Math.max(1, rows.length) + (focused ? 1 : 0) + (focused?.hint ? 1 : 0) + 1 + 1 + 1) - (unsaved === void 0 ? 0 : 1)
|
|
20999
|
+
);
|
|
21000
|
+
const firstCommand = Math.max(0, (palette?.row ?? 0) - paletteRoom + 1);
|
|
21001
|
+
const formWidth = Math.min(FORM_WIDTH, columns);
|
|
21002
|
+
const formLabels = dialog?.kind === "form" ? formRows(dialog.editing === void 0) : [];
|
|
21003
|
+
const formLabelWidth = Math.max(0, ...formLabels.map(({ label }) => label.length));
|
|
20452
21004
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
20453
21005
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.bold(cut(summary)) }),
|
|
20454
21006
|
rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
20455
21007
|
index === current ? marker : " ",
|
|
20456
21008
|
row
|
|
20457
21009
|
] }, results[index]?.name)),
|
|
21010
|
+
rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim(cut(empty)) }),
|
|
20458
21011
|
focused && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim(
|
|
20459
21012
|
cut(focused.error ? `${focused.target} ${dot} ${focused.error}` : focused.target)
|
|
20460
21013
|
) }),
|
|
21014
|
+
focused?.hint && panel?.kind !== "service" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim(cut(focused.hint)) }),
|
|
20461
21015
|
panel !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
20462
21016
|
Box_default,
|
|
20463
21017
|
{
|
|
@@ -20465,20 +21019,55 @@ function Dashboard({
|
|
|
20465
21019
|
width: panelWidth,
|
|
20466
21020
|
borderStyle: unicode ? "single" : "classic",
|
|
20467
21021
|
paddingX: 1,
|
|
21022
|
+
children: [
|
|
21023
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.bold(truncate(panel.kind === "options" ? "Options" : panel.name, panelRoom)) }),
|
|
21024
|
+
detail.map((line) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: toned(line) }, line.id)),
|
|
21025
|
+
detail.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim((unicode ? "\u2500" : "-").repeat(panelRoom)) }),
|
|
21026
|
+
panelRows.map(([label, value], index) => {
|
|
21027
|
+
const line = `${index === panel.row ? marker : " "}${label.padEnd(labelWidth)} ${unicode ? `\u2039 ${value} \u203A` : `< ${value} >`}`;
|
|
21028
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: truncate(line, panelRoom) }, label);
|
|
21029
|
+
})
|
|
21030
|
+
]
|
|
21031
|
+
}
|
|
21032
|
+
),
|
|
21033
|
+
filtering && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cut(`Filter: ${filter}${CURSOR}`) }),
|
|
21034
|
+
palette && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cut(`: ${palette.query}${CURSOR}`) }),
|
|
21035
|
+
palette && commands.slice(firstCommand, firstCommand + paletteRoom).map(({ command, label, keys }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cut(
|
|
21036
|
+
`${commands[palette.row]?.command === command ? marker : " "}${label.padEnd(commandWidth)} ${keys}`
|
|
21037
|
+
) }, command)),
|
|
21038
|
+
palette && commands.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim(cut(`no command matches "${palette.query}"`)) }),
|
|
21039
|
+
dialog?.kind === "confirm" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.bold(cut(`Remove ${dialog.name} from the config?`)) }),
|
|
21040
|
+
dialog?.kind === "form" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
21041
|
+
Box_default,
|
|
21042
|
+
{
|
|
21043
|
+
flexDirection: "column",
|
|
21044
|
+
width: formWidth,
|
|
21045
|
+
borderStyle: unicode ? "single" : "classic",
|
|
21046
|
+
paddingX: 1,
|
|
20468
21047
|
children: [
|
|
20469
21048
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.bold(
|
|
20470
21049
|
truncate(
|
|
20471
|
-
|
|
20472
|
-
Math.max(1,
|
|
21050
|
+
dialog.editing === void 0 ? "Add a service" : `Edit ${dialog.editing}`,
|
|
21051
|
+
Math.max(1, formWidth - PANEL_FRAME_WIDTH)
|
|
20473
21052
|
)
|
|
20474
21053
|
) }),
|
|
20475
|
-
|
|
20476
|
-
const
|
|
20477
|
-
|
|
20478
|
-
|
|
21054
|
+
formLabels.map(({ key, label, placeholder }, index) => {
|
|
21055
|
+
const active = index === dialog.row;
|
|
21056
|
+
const value = dialog.values[key];
|
|
21057
|
+
const room = Math.max(1, formWidth - PANEL_FRAME_WIDTH);
|
|
21058
|
+
const prefix = `${active ? marker : " "}${label.padEnd(formLabelWidth)} `;
|
|
21059
|
+
const typed = active ? `${prefix}${ending(value, Math.max(1, room - prefix.length - CURSOR.length))}${CURSOR}` : truncate(`${prefix}${value}`, room);
|
|
21060
|
+
const example = value ? "" : truncate(placeholder, Math.max(0, room - typed.length - 1));
|
|
21061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
21062
|
+
typed,
|
|
21063
|
+
example && colors.dim(`${active ? " " : ""}${example}`)
|
|
21064
|
+
] }, key);
|
|
21065
|
+
}),
|
|
21066
|
+
dialog.problem !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.yellow(dialog.problem) })
|
|
20479
21067
|
]
|
|
20480
21068
|
}
|
|
20481
21069
|
),
|
|
21070
|
+
notice !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: notice }),
|
|
20482
21071
|
unsaved !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.yellow(cut(`not saved: ${unsaved}`)) }),
|
|
20483
21072
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: colors.dim(cut(hint)) })
|
|
20484
21073
|
] });
|
|
@@ -20486,7 +21075,14 @@ function Dashboard({
|
|
|
20486
21075
|
|
|
20487
21076
|
// src/dashboard/open.tsx
|
|
20488
21077
|
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
20489
|
-
|
|
21078
|
+
var pickFields = ({ host, port, path, expect }) => ({
|
|
21079
|
+
host,
|
|
21080
|
+
port,
|
|
21081
|
+
path,
|
|
21082
|
+
expect
|
|
21083
|
+
});
|
|
21084
|
+
async function openDashboard(initial, terminal, { onServiceWrite, ...state }) {
|
|
21085
|
+
let config = initial;
|
|
20490
21086
|
const check2 = (signal, timeoutMs, skipped, timeouts2) => {
|
|
20491
21087
|
const services = Object.fromEntries(
|
|
20492
21088
|
Object.entries(config.services).map(([name, service]) => [
|
|
@@ -20494,7 +21090,7 @@ async function openDashboard(config, terminal, state) {
|
|
|
20494
21090
|
{ ...service, timeout: timeouts2[name] }
|
|
20495
21091
|
])
|
|
20496
21092
|
);
|
|
20497
|
-
return runChecks({ ...config, services }, [], { timeoutMs, signal }, skipped);
|
|
21093
|
+
return runChecks({ ...config, services }, [], { timeoutMs, signal, detail: true }, skipped);
|
|
20498
21094
|
};
|
|
20499
21095
|
const timeouts = Object.fromEntries(
|
|
20500
21096
|
Object.entries(config.services).flatMap(
|
|
@@ -20502,7 +21098,25 @@ async function openDashboard(config, terminal, state) {
|
|
|
20502
21098
|
)
|
|
20503
21099
|
);
|
|
20504
21100
|
const app = render_default(
|
|
20505
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
21101
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
21102
|
+
Dashboard,
|
|
21103
|
+
{
|
|
21104
|
+
terminal,
|
|
21105
|
+
check: check2,
|
|
21106
|
+
timeouts,
|
|
21107
|
+
...serviceActions(() => config),
|
|
21108
|
+
...state,
|
|
21109
|
+
serviceFields: (name) => {
|
|
21110
|
+
const service = config.services[name];
|
|
21111
|
+
return service && pickFields(service);
|
|
21112
|
+
},
|
|
21113
|
+
onServiceWrite: onServiceWrite && (async (write) => {
|
|
21114
|
+
const { config: written2, ...outcome } = await onServiceWrite(write);
|
|
21115
|
+
if (written2) config = written2;
|
|
21116
|
+
return { ...outcome, saved: written2 !== void 0 };
|
|
21117
|
+
})
|
|
21118
|
+
}
|
|
21119
|
+
),
|
|
20506
21120
|
{ alternateScreen: true }
|
|
20507
21121
|
);
|
|
20508
21122
|
await app.waitUntilExit();
|