@thingd/cli 0.56.0 → 0.57.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/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +151 -5
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../src/interactive.ts"],"names":[],"mappings":"AA8lGA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA0FvD"}
|
package/dist/interactive.js
CHANGED
|
@@ -92,6 +92,7 @@ let objectWriteRateHistory = [];
|
|
|
92
92
|
let eventAppendRateHistory = [];
|
|
93
93
|
let viewerLines = ["Select an item to view details."];
|
|
94
94
|
let viewerScroll = 0;
|
|
95
|
+
let showHelp = false;
|
|
95
96
|
let lastNeighborsRef = "";
|
|
96
97
|
let loadedItemId = "";
|
|
97
98
|
let loadTimer = null;
|
|
@@ -915,6 +916,38 @@ async function loadContent(node) {
|
|
|
915
916
|
content += ` ${pc.dim("Links".padEnd(14))} ${pc.blue(String(totalLinksCount).padEnd(6))} ${pc.dim("total")}\n`;
|
|
916
917
|
content += ` ${pc.dim("Active Jobs".padEnd(14))} ${pc.yellow(String(totalActiveJobsCount).padEnd(6))} ${pc.dim("in flight")}\n`;
|
|
917
918
|
content += ` ${pc.dim("Dead Jobs".padEnd(14))} ${pc.red(String(totalDeadJobsCount).padEnd(6))} ${pc.dim("failed")}\n\n`;
|
|
919
|
+
// Collection breakdown bar chart
|
|
920
|
+
if (collections.length > 0) {
|
|
921
|
+
content += ` ${pc.bold("Collections")}\n`;
|
|
922
|
+
const maxCount = Math.max(1, ...collections.map((c) => (objectsByCollection.get(c) ?? []).length));
|
|
923
|
+
const barMax = Math.max(5, viewW - 30);
|
|
924
|
+
for (const col of collections) {
|
|
925
|
+
const count = (objectsByCollection.get(col) ?? []).length;
|
|
926
|
+
const barLen = Math.max(1, Math.round((count / maxCount) * barMax));
|
|
927
|
+
const bar = barLen > 0 ? pc.cyan("█".repeat(barLen)) : "";
|
|
928
|
+
content += ` ${pc.dim(col.slice(0, 12).padEnd(12))} ${bar} ${pc.dim(String(count))}\n`;
|
|
929
|
+
}
|
|
930
|
+
content += "\n";
|
|
931
|
+
}
|
|
932
|
+
// Capacity gauge bar
|
|
933
|
+
const totalHistorical = totalObjects +
|
|
934
|
+
totalEventsCount +
|
|
935
|
+
totalLinksCount +
|
|
936
|
+
totalActiveJobsCount +
|
|
937
|
+
totalDeadJobsCount;
|
|
938
|
+
if (totalHistorical > 0) {
|
|
939
|
+
const capBarW = Math.max(5, viewW - 20);
|
|
940
|
+
const objFrac = totalObjects / totalHistorical;
|
|
941
|
+
const evtFrac = totalEventsCount / totalHistorical;
|
|
942
|
+
const linkFrac = totalLinksCount / totalHistorical;
|
|
943
|
+
const objChars = Math.round(objFrac * capBarW);
|
|
944
|
+
const evtChars = Math.round(evtFrac * capBarW);
|
|
945
|
+
const linkChars = Math.round(linkFrac * capBarW);
|
|
946
|
+
const jobChars = capBarW - objChars - evtChars - linkChars;
|
|
947
|
+
content += ` ${pc.bold("Distribution")}\n`;
|
|
948
|
+
content += ` ${pc.dim(" ")}${pc.cyan("█".repeat(Math.max(0, objChars)))}${pc.green("█".repeat(Math.max(0, evtChars)))}${pc.blue("█".repeat(Math.max(0, linkChars)))}${pc.yellow("█".repeat(Math.max(0, jobChars)))}\n`;
|
|
949
|
+
content += ` ${pc.cyan("■")} objs ${pc.green("■")} evts ${pc.blue("■")} links ${pc.yellow("■")} jobs\n\n`;
|
|
950
|
+
}
|
|
918
951
|
content += ` ${pc.bold("Connection")}\n`;
|
|
919
952
|
content += ` ${pc.dim("Driver".padEnd(14))} ${driverName}\n`;
|
|
920
953
|
content += ` ${pc.dim("Path".padEnd(14))} ${dbPath || ":memory:"}\n`;
|
|
@@ -998,14 +1031,20 @@ function draw() {
|
|
|
998
1031
|
scrollOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, tree.length - bodyH)));
|
|
999
1032
|
let buf = "\u001B[H"; // Move to top-left
|
|
1000
1033
|
// Header — opencode style: clean, no inverse bar
|
|
1001
|
-
if (
|
|
1002
|
-
buf += ` ${pc.cyan("◈")} ${pc.bold("thingd")} ${pc.dim("
|
|
1034
|
+
if (showHelp) {
|
|
1035
|
+
buf += ` ${pc.cyan("◈")} ${pc.bold("thingd")} ${pc.dim("Help (press any key)")}\n`;
|
|
1036
|
+
}
|
|
1037
|
+
else if (!connected) {
|
|
1038
|
+
buf += ` ${pc.cyan("◈")} ${pc.bold("thingd")} ${pc.dim("Select Environment")} ${pc.dim("[?] help")}\n`;
|
|
1003
1039
|
}
|
|
1004
1040
|
else if (formState?.active) {
|
|
1005
|
-
buf += ` ${pc.cyan("◈")} ${pc.bold("thingd")} ${pc.cyan(driver.toUpperCase())} ${pc.dim("Input Mode")}\n`;
|
|
1041
|
+
buf += ` ${pc.cyan("◈")} ${pc.bold("thingd")} ${pc.cyan(driver.toUpperCase())} ${pc.dim("Input Mode")} ${pc.dim("[?] help")}\n`;
|
|
1006
1042
|
}
|
|
1007
1043
|
else {
|
|
1008
|
-
const
|
|
1044
|
+
const breadcrumb = computeBreadcrumbs();
|
|
1045
|
+
const base = ` ${pc.cyan("◈")} ${pc.bold("thingd")} ${pc.cyan(driver.toUpperCase())}`;
|
|
1046
|
+
const dash = breadcrumb ? ` ${pc.dim("▸")} ` : "";
|
|
1047
|
+
const label = `${base}${dash}${breadcrumb} ${pc.dim("[?] help")}`;
|
|
1009
1048
|
buf += `${padToWidth(label, W)}\n`;
|
|
1010
1049
|
}
|
|
1011
1050
|
buf += `${pc.dim("─".repeat(W))}\n`;
|
|
@@ -1097,8 +1136,101 @@ function draw() {
|
|
|
1097
1136
|
buf += padToWidth(help, W);
|
|
1098
1137
|
// Clear to end
|
|
1099
1138
|
buf += "\u001B[J";
|
|
1139
|
+
if (showHelp) {
|
|
1140
|
+
buf += computeHelpOverlay(W, H);
|
|
1141
|
+
}
|
|
1100
1142
|
process.stdout.write(buf);
|
|
1101
1143
|
}
|
|
1144
|
+
function computeBreadcrumbs() {
|
|
1145
|
+
if (!connected) {
|
|
1146
|
+
return "";
|
|
1147
|
+
}
|
|
1148
|
+
const tree = buildTree();
|
|
1149
|
+
const node = tree[cursorIndex];
|
|
1150
|
+
if (!node) {
|
|
1151
|
+
return "";
|
|
1152
|
+
}
|
|
1153
|
+
const labels = [];
|
|
1154
|
+
const climb = (id) => {
|
|
1155
|
+
if (!id) {
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
const n = tree.find((t) => t.id === id);
|
|
1159
|
+
if (!n) {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
if (n.parentId) {
|
|
1163
|
+
climb(n.parentId);
|
|
1164
|
+
}
|
|
1165
|
+
const clean = n.label
|
|
1166
|
+
.replace(/[▾▸●○·◇◈◉⬡]/g, "")
|
|
1167
|
+
.trim()
|
|
1168
|
+
.replace(/^\S+\s+/, "")
|
|
1169
|
+
.trim();
|
|
1170
|
+
if (clean && !clean.startsWith("(")) {
|
|
1171
|
+
labels.push(clean);
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
climb(node.id);
|
|
1175
|
+
return labels.length > 0 ? pc.dim(labels.join(" > ")) : "";
|
|
1176
|
+
}
|
|
1177
|
+
function computeHelpOverlay(W, H) {
|
|
1178
|
+
const lines = [
|
|
1179
|
+
`${pc.bold(" thingd TUI Help")}`,
|
|
1180
|
+
"",
|
|
1181
|
+
` ${pc.bold("Navigation")}`,
|
|
1182
|
+
` ${pc.dim("↑↓/j/k")} Move cursor`,
|
|
1183
|
+
` ${pc.dim("←→/h/l")} Expand/collapse tree`,
|
|
1184
|
+
` ${pc.dim("Enter")} Toggle expand`,
|
|
1185
|
+
"",
|
|
1186
|
+
` ${pc.bold("Operations")}`,
|
|
1187
|
+
` ${pc.dim("c")} Create resource`,
|
|
1188
|
+
` ${pc.dim("e")} Edit object/job`,
|
|
1189
|
+
` ${pc.dim("d")} Delete object/link/job`,
|
|
1190
|
+
` ${pc.dim("r")} Refresh data`,
|
|
1191
|
+
` ${pc.dim("i")} Connection info`,
|
|
1192
|
+
"",
|
|
1193
|
+
` ${pc.bold("Data Views")}`,
|
|
1194
|
+
` ${pc.dim("n")} Object neighbors (links)`,
|
|
1195
|
+
` ${pc.dim("a")} Aggregate (count/sum/avg/min/max)`,
|
|
1196
|
+
` ${pc.dim("t")} Time-series query`,
|
|
1197
|
+
` ${pc.dim("N")} Natural language query`,
|
|
1198
|
+
` ${pc.dim("/f")} Search objects`,
|
|
1199
|
+
` ${pc.dim("o")} Object listing options`,
|
|
1200
|
+
` ${pc.dim("b")} Batch put/delete`,
|
|
1201
|
+
"",
|
|
1202
|
+
` ${pc.bold("System")}`,
|
|
1203
|
+
` ${pc.dim("s")} Switch driver`,
|
|
1204
|
+
` ${pc.dim("m")} Maintenance`,
|
|
1205
|
+
` ${pc.dim("l")} Logout`,
|
|
1206
|
+
` ${pc.dim("q")} Quit`,
|
|
1207
|
+
` ${pc.dim("?")} Toggle this help`,
|
|
1208
|
+
"",
|
|
1209
|
+
` ${pc.dim("Press any key to close help.")}`,
|
|
1210
|
+
];
|
|
1211
|
+
const helpW = 44;
|
|
1212
|
+
const helpH = lines.length + 2;
|
|
1213
|
+
const col = Math.max(0, Math.floor((W - helpW) / 2));
|
|
1214
|
+
const row = Math.max(0, Math.floor((H - helpH) / 2));
|
|
1215
|
+
let out = "";
|
|
1216
|
+
for (let r = 0; r < H; r++) {
|
|
1217
|
+
if (r >= row && r < row + helpH) {
|
|
1218
|
+
const lineIdx = r - row;
|
|
1219
|
+
if (lineIdx === 0 || lineIdx === helpH - 1) {
|
|
1220
|
+
out += `${" ".repeat(col) + pc.dim(`┌${"─".repeat(helpW - 2)}┐`)}\n`;
|
|
1221
|
+
}
|
|
1222
|
+
else {
|
|
1223
|
+
const text = lines[lineIdx - 1] ?? "";
|
|
1224
|
+
const padded = text + " ".repeat(Math.max(0, helpW - visibleWidth(text) - 2));
|
|
1225
|
+
out += `${" ".repeat(col) + pc.dim("│")} ${padded} ${pc.dim("│")}\n`;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
out += "\n";
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return out;
|
|
1233
|
+
}
|
|
1102
1234
|
/** Pad/truncate `text` to exactly `width` visible characters. */
|
|
1103
1235
|
function fitToWidth(text, width, highlight) {
|
|
1104
1236
|
const vw = visibleWidth(text);
|
|
@@ -2105,6 +2237,12 @@ function setupKeypress() {
|
|
|
2105
2237
|
if (!key) {
|
|
2106
2238
|
return;
|
|
2107
2239
|
}
|
|
2240
|
+
// Help overlay dismisses on any keypress
|
|
2241
|
+
if (showHelp) {
|
|
2242
|
+
showHelp = false;
|
|
2243
|
+
draw();
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2108
2246
|
// Quit
|
|
2109
2247
|
if ((key.ctrl && key.name === "c") || key.name === "q") {
|
|
2110
2248
|
if (formState?.active && key.name !== "q") {
|
|
@@ -2221,13 +2359,17 @@ function setupKeypress() {
|
|
|
2221
2359
|
}
|
|
2222
2360
|
}
|
|
2223
2361
|
else if (!connected) {
|
|
2224
|
-
// Driver selection mode — only Enter
|
|
2362
|
+
// Driver selection mode — only Enter and ? work
|
|
2225
2363
|
if (key.name === "return") {
|
|
2226
2364
|
const node = tree[cursorIndex];
|
|
2227
2365
|
if (node) {
|
|
2228
2366
|
await handleConnect(node);
|
|
2229
2367
|
}
|
|
2230
2368
|
}
|
|
2369
|
+
else if (str === "?") {
|
|
2370
|
+
showHelp = !showHelp;
|
|
2371
|
+
draw();
|
|
2372
|
+
}
|
|
2231
2373
|
}
|
|
2232
2374
|
else {
|
|
2233
2375
|
// Connected mode — full set of shortcuts
|
|
@@ -2313,6 +2455,10 @@ function setupKeypress() {
|
|
|
2313
2455
|
else if (str === "/" || str === "f" || str === "F") {
|
|
2314
2456
|
await handleSearch();
|
|
2315
2457
|
}
|
|
2458
|
+
else if (str === "?") {
|
|
2459
|
+
showHelp = !showHelp;
|
|
2460
|
+
draw();
|
|
2461
|
+
}
|
|
2316
2462
|
else if (str === "i" || str === "I") {
|
|
2317
2463
|
await handleInfo();
|
|
2318
2464
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thingd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "CLI, Interactive TUI Dashboard, and MCP server for thingd — a fast object-first data engine for applications and AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://engine.thingd.cloud",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"cli-table3": "^0.6.5",
|
|
46
46
|
"picocolors": "^1.1.1",
|
|
47
47
|
"zod": "^4.4.3",
|
|
48
|
-
"@thingd/sdk": "0.
|
|
48
|
+
"@thingd/sdk": "0.57.0"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24.0.0"
|