json-term 1.0.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/cli.js ADDED
@@ -0,0 +1,718 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_commander = require("commander");
28
+ var import_fs = __toESM(require("fs"));
29
+ var import_picocolors4 = __toESM(require("picocolors"));
30
+
31
+ // src/formatter.ts
32
+ var import_picocolors = __toESM(require("picocolors"));
33
+ function getValueType(value) {
34
+ if (value === null || value === void 0) return "null";
35
+ if (Array.isArray(value)) return "array";
36
+ const t = typeof value;
37
+ if (t === "object") return "object";
38
+ if (t === "string") return "string";
39
+ if (t === "number") return "number";
40
+ if (t === "boolean") return "boolean";
41
+ return "string";
42
+ }
43
+ function formatValue(value, truncateLen = 80) {
44
+ const type = getValueType(value);
45
+ switch (type) {
46
+ case "string": {
47
+ const str = JSON.stringify(value);
48
+ const truncated = str.length > truncateLen ? str.slice(0, truncateLen - 3) + '..."' : str;
49
+ return import_picocolors.default.green(truncated);
50
+ }
51
+ case "number":
52
+ return import_picocolors.default.yellow(String(value));
53
+ case "boolean":
54
+ return import_picocolors.default.magenta(String(value));
55
+ case "null":
56
+ return import_picocolors.default.dim("null");
57
+ case "array":
58
+ return import_picocolors.default.dim(`[${value.length} items]`);
59
+ case "object":
60
+ return import_picocolors.default.dim(`{${Object.keys(value).length} keys}`);
61
+ default:
62
+ return String(value);
63
+ }
64
+ }
65
+ function formatKey(key) {
66
+ return import_picocolors.default.bold(import_picocolors.default.cyan(key));
67
+ }
68
+ function buildTree(data, key, path = "$", depth = 0, maxInitialDepth = 2, parentId) {
69
+ const type = getValueType(data);
70
+ const id = `${path}-${key || "root"}-${depth}`;
71
+ const collapsed = depth >= maxInitialDepth;
72
+ const node = {
73
+ id,
74
+ key,
75
+ value: data,
76
+ type,
77
+ depth,
78
+ path,
79
+ collapsed,
80
+ parentId
81
+ };
82
+ if (type === "object" && data !== null) {
83
+ const keys = Object.keys(data);
84
+ node.itemCount = keys.length;
85
+ node.children = keys.map(
86
+ (k) => buildTree(
87
+ data[k],
88
+ k,
89
+ path === "$" ? k : `${path}.${k}`,
90
+ depth + 1,
91
+ maxInitialDepth,
92
+ id
93
+ )
94
+ );
95
+ } else if (type === "array") {
96
+ node.itemCount = data.length;
97
+ node.children = data.map(
98
+ (item, idx) => buildTree(
99
+ item,
100
+ `[${idx}]`,
101
+ `${path}[${idx}]`,
102
+ depth + 1,
103
+ maxInitialDepth,
104
+ id
105
+ )
106
+ );
107
+ }
108
+ return node;
109
+ }
110
+ function prettyPrintTree(node, indent = "", isLast = true) {
111
+ const isRoot = node.depth === 0;
112
+ const marker = node.type === "object" || node.type === "array" ? node.collapsed ? import_picocolors.default.cyan("\u25B6 ") : import_picocolors.default.cyan("\u25BC ") : import_picocolors.default.dim("\u2022 ");
113
+ let line = "";
114
+ if (!isRoot) {
115
+ const branch = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
116
+ line += import_picocolors.default.dim(indent + branch) + marker;
117
+ if (node.key) {
118
+ line += formatKey(node.key) + import_picocolors.default.dim(": ");
119
+ }
120
+ } else {
121
+ line += marker + (node.key ? formatKey(node.key) + import_picocolors.default.dim(": ") : import_picocolors.default.bold(import_picocolors.default.white("root: ")));
122
+ }
123
+ if (node.type === "object") {
124
+ line += import_picocolors.default.dim(`{ ${node.itemCount} keys }`);
125
+ } else if (node.type === "array") {
126
+ line += import_picocolors.default.dim(`[ ${node.itemCount} items ]`);
127
+ } else {
128
+ line += formatValue(node.value);
129
+ }
130
+ const lines = [line];
131
+ if (!node.collapsed && node.children && node.children.length > 0) {
132
+ const nextIndent = isRoot ? "" : indent + (isLast ? " " : "\u2502 ");
133
+ node.children.forEach((child, index) => {
134
+ const childIsLast = index === node.children.length - 1;
135
+ lines.push(prettyPrintTree(child, nextIndent, childIsLast));
136
+ });
137
+ }
138
+ return lines.join("\n");
139
+ }
140
+
141
+ // src/differ.ts
142
+ var import_picocolors2 = __toESM(require("picocolors"));
143
+ function diffJSON(a, b, key, path = "$", depth = 0, maxInitialDepth = 3) {
144
+ const typeA = getValueType(a);
145
+ const typeB = getValueType(b);
146
+ const id = `${path}-${key || "diff"}-${depth}`;
147
+ const collapsed = depth >= maxInitialDepth;
148
+ if (a === void 0 && b !== void 0) {
149
+ const node = {
150
+ id,
151
+ key,
152
+ path,
153
+ depth,
154
+ status: "added",
155
+ newValue: b,
156
+ type: typeB,
157
+ collapsed: false
158
+ };
159
+ if (typeB === "object" && b !== null) {
160
+ node.itemCount = Object.keys(b).length;
161
+ node.children = Object.keys(b).map(
162
+ (k) => diffJSON(void 0, b[k], k, path === "$" ? k : `${path}.${k}`, depth + 1, maxInitialDepth)
163
+ );
164
+ } else if (typeB === "array") {
165
+ node.itemCount = b.length;
166
+ node.children = b.map(
167
+ (item, idx) => diffJSON(void 0, item, `[${idx}]`, `${path}[${idx}]`, depth + 1, maxInitialDepth)
168
+ );
169
+ }
170
+ return node;
171
+ }
172
+ if (a !== void 0 && b === void 0) {
173
+ const node = {
174
+ id,
175
+ key,
176
+ path,
177
+ depth,
178
+ status: "removed",
179
+ oldValue: a,
180
+ type: typeA,
181
+ collapsed: false
182
+ };
183
+ if (typeA === "object" && a !== null) {
184
+ node.itemCount = Object.keys(a).length;
185
+ node.children = Object.keys(a).map(
186
+ (k) => diffJSON(a[k], void 0, k, path === "$" ? k : `${path}.${k}`, depth + 1, maxInitialDepth)
187
+ );
188
+ } else if (typeA === "array") {
189
+ node.itemCount = a.length;
190
+ node.children = a.map(
191
+ (item, idx) => diffJSON(item, void 0, `[${idx}]`, `${path}[${idx}]`, depth + 1, maxInitialDepth)
192
+ );
193
+ }
194
+ return node;
195
+ }
196
+ if (typeA !== typeB) {
197
+ return {
198
+ id,
199
+ key,
200
+ path,
201
+ depth,
202
+ status: "modified",
203
+ oldValue: a,
204
+ newValue: b,
205
+ type: typeB,
206
+ collapsed: false
207
+ };
208
+ }
209
+ if (typeA === "object" && a !== null && b !== null) {
210
+ const allKeys = Array.from(/* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]));
211
+ const children = [];
212
+ let hasModifications = false;
213
+ for (const k of allKeys) {
214
+ const child = diffJSON(
215
+ a[k],
216
+ b[k],
217
+ k,
218
+ path === "$" ? k : `${path}.${k}`,
219
+ depth + 1,
220
+ maxInitialDepth
221
+ );
222
+ if (child.status !== "unchanged") hasModifications = true;
223
+ children.push(child);
224
+ }
225
+ return {
226
+ id,
227
+ key,
228
+ path,
229
+ depth,
230
+ status: hasModifications ? "modified" : "unchanged",
231
+ oldValue: a,
232
+ newValue: b,
233
+ type: "object",
234
+ itemCount: allKeys.length,
235
+ collapsed,
236
+ children
237
+ };
238
+ }
239
+ if (typeA === "array") {
240
+ const maxLen = Math.max(a.length, b.length);
241
+ const children = [];
242
+ let hasModifications = false;
243
+ for (let i = 0; i < maxLen; i++) {
244
+ const child = diffJSON(
245
+ a[i],
246
+ b[i],
247
+ `[${i}]`,
248
+ `${path}[${i}]`,
249
+ depth + 1,
250
+ maxInitialDepth
251
+ );
252
+ if (child.status !== "unchanged") hasModifications = true;
253
+ children.push(child);
254
+ }
255
+ return {
256
+ id,
257
+ key,
258
+ path,
259
+ depth,
260
+ status: hasModifications ? "modified" : "unchanged",
261
+ oldValue: a,
262
+ newValue: b,
263
+ type: "array",
264
+ itemCount: maxLen,
265
+ collapsed,
266
+ children
267
+ };
268
+ }
269
+ if (a !== b) {
270
+ return {
271
+ id,
272
+ key,
273
+ path,
274
+ depth,
275
+ status: "modified",
276
+ oldValue: a,
277
+ newValue: b,
278
+ type: typeB,
279
+ collapsed: false
280
+ };
281
+ }
282
+ return {
283
+ id,
284
+ key,
285
+ path,
286
+ depth,
287
+ status: "unchanged",
288
+ oldValue: a,
289
+ newValue: b,
290
+ type: typeA,
291
+ collapsed
292
+ };
293
+ }
294
+ function calculateDiffStats(node) {
295
+ const stats = { added: 0, removed: 0, modified: 0, unchanged: 0 };
296
+ function traverse(n) {
297
+ if (n.children && n.children.length > 0) {
298
+ n.children.forEach(traverse);
299
+ } else {
300
+ stats[n.status]++;
301
+ }
302
+ }
303
+ traverse(node);
304
+ return stats;
305
+ }
306
+ function prettyPrintDiff(node, options = {}, indent = "", isLast = true) {
307
+ const showUnchanged = options.showUnchanged ?? true;
308
+ if (!showUnchanged && node.status === "unchanged" && (!node.children || node.children.length === 0)) {
309
+ return "";
310
+ }
311
+ const isRoot = node.depth === 0;
312
+ let prefix = " ";
313
+ let statusColor = import_picocolors2.default.dim;
314
+ if (node.status === "added") {
315
+ prefix = import_picocolors2.default.green("+ ");
316
+ statusColor = import_picocolors2.default.green;
317
+ } else if (node.status === "removed") {
318
+ prefix = import_picocolors2.default.red("- ");
319
+ statusColor = import_picocolors2.default.red;
320
+ } else if (node.status === "modified") {
321
+ prefix = import_picocolors2.default.yellow("~ ");
322
+ statusColor = import_picocolors2.default.yellow;
323
+ }
324
+ let line = prefix;
325
+ if (!isRoot) {
326
+ const branch = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
327
+ line += import_picocolors2.default.dim(indent + branch);
328
+ if (node.key) {
329
+ line += formatKey(node.key) + import_picocolors2.default.dim(": ");
330
+ }
331
+ } else {
332
+ line += node.key ? formatKey(node.key) + import_picocolors2.default.dim(": ") : import_picocolors2.default.bold("diff: ");
333
+ }
334
+ if (node.status === "added") {
335
+ line += statusColor(formatValue(node.newValue));
336
+ } else if (node.status === "removed") {
337
+ line += statusColor(formatValue(node.oldValue));
338
+ } else if (node.status === "modified") {
339
+ if (node.type === "object" || node.type === "array") {
340
+ line += statusColor(`{ ${node.itemCount} items modified }`);
341
+ } else {
342
+ line += `${import_picocolors2.default.red(formatValue(node.oldValue))} ${import_picocolors2.default.dim("->")} ${import_picocolors2.default.green(formatValue(node.newValue))}`;
343
+ }
344
+ } else {
345
+ line += import_picocolors2.default.dim(formatValue(node.newValue));
346
+ }
347
+ const lines = [line];
348
+ if (!node.collapsed && node.children && node.children.length > 0) {
349
+ const nextIndent = isRoot ? "" : indent + (isLast ? " " : "\u2502 ");
350
+ node.children.forEach((child, index) => {
351
+ const childIsLast = index === node.children.length - 1;
352
+ const childStr = prettyPrintDiff(child, options, nextIndent, childIsLast);
353
+ if (childStr) lines.push(childStr);
354
+ });
355
+ }
356
+ return lines.join("\n");
357
+ }
358
+
359
+ // src/interactive.ts
360
+ var import_readline = __toESM(require("readline"));
361
+ var import_picocolors3 = __toESM(require("picocolors"));
362
+
363
+ // src/utils/clipboard.ts
364
+ var import_child_process = require("child_process");
365
+ var import_os = __toESM(require("os"));
366
+ async function copyToClipboard(text) {
367
+ const platform = import_os.default.platform();
368
+ return new Promise((resolve) => {
369
+ let proc;
370
+ if (platform === "darwin") {
371
+ proc = (0, import_child_process.exec)("pbcopy", (err) => resolve(!err));
372
+ } else if (platform === "win32") {
373
+ proc = (0, import_child_process.exec)("clip", (err) => resolve(!err));
374
+ } else {
375
+ proc = (0, import_child_process.exec)("wl-copy 2>/dev/null || xclip -selection clipboard 2>/dev/null || xsel --clipboard --input", (err) => {
376
+ resolve(!err);
377
+ });
378
+ }
379
+ if (proc.stdin) {
380
+ proc.stdin.write(text);
381
+ proc.stdin.end();
382
+ } else {
383
+ resolve(false);
384
+ }
385
+ });
386
+ }
387
+
388
+ // src/interactive.ts
389
+ var InteractiveViewer = class {
390
+ root;
391
+ flatList = [];
392
+ selectedIndex = 0;
393
+ searchQuery = "";
394
+ isSearching = false;
395
+ searchInput = "";
396
+ notification = "";
397
+ notificationTimer = null;
398
+ isDiff;
399
+ constructor(root, isDiff = false) {
400
+ this.root = root;
401
+ this.isDiff = isDiff;
402
+ this.rebuildFlatList();
403
+ }
404
+ isDiffNode(node) {
405
+ return this.isDiff && "status" in node;
406
+ }
407
+ rebuildFlatList() {
408
+ const list = [];
409
+ const traverse = (node, currentDepth) => {
410
+ if (this.searchQuery) {
411
+ const query = this.searchQuery.toLowerCase();
412
+ const keyMatch = node.key && node.key.toLowerCase().includes(query);
413
+ const pathMatch = node.path && node.path.toLowerCase().includes(query);
414
+ const valMatch = "value" in node ? JSON.stringify(node.value).toLowerCase().includes(query) : "newValue" in node ? JSON.stringify(node.newValue).toLowerCase().includes(query) : false;
415
+ if (!keyMatch && !pathMatch && !valMatch && (!node.children || node.children.length === 0)) {
416
+ return;
417
+ }
418
+ }
419
+ list.push({ node, depth: currentDepth, isDiff: this.isDiff });
420
+ if (!node.collapsed && node.children && node.children.length > 0) {
421
+ for (const child of node.children) {
422
+ traverse(child, currentDepth + 1);
423
+ }
424
+ }
425
+ };
426
+ traverse(this.root, 0);
427
+ this.flatList = list;
428
+ if (this.selectedIndex >= this.flatList.length) {
429
+ this.selectedIndex = Math.max(0, this.flatList.length - 1);
430
+ }
431
+ }
432
+ setNotification(msg) {
433
+ this.notification = msg;
434
+ if (this.notificationTimer) clearTimeout(this.notificationTimer);
435
+ this.notificationTimer = setTimeout(() => {
436
+ this.notification = "";
437
+ this.render();
438
+ }, 2500);
439
+ }
440
+ render() {
441
+ const rows = process.stdout.rows || 24;
442
+ const maxVisibleRows = Math.max(5, rows - 5);
443
+ let startIdx = 0;
444
+ if (this.selectedIndex >= maxVisibleRows) {
445
+ startIdx = this.selectedIndex - maxVisibleRows + 1;
446
+ }
447
+ const endIdx = Math.min(this.flatList.length, startIdx + maxVisibleRows);
448
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H\x1B[?25l");
449
+ const title = this.isDiff ? " JSON Structural Diff Explorer " : " Interactive JSON Tree Explorer ";
450
+ console.log(import_picocolors3.default.bgCyan(import_picocolors3.default.black(import_picocolors3.default.bold(` ${title} `)) + import_picocolors3.default.dim(` [Total items: ${this.flatList.length}]`)));
451
+ for (let i = startIdx; i < endIdx; i++) {
452
+ const { node, depth } = this.flatList[i];
453
+ const isSelected = i === this.selectedIndex;
454
+ const indent = " ".repeat(depth);
455
+ let marker = "\u2022 ";
456
+ if (node.children && node.children.length > 0) {
457
+ marker = node.collapsed ? "\u25B6 " : "\u25BC ";
458
+ }
459
+ let lineContent = "";
460
+ if (this.isDiffNode(node)) {
461
+ let diffPrefix = " ";
462
+ if (node.status === "added") diffPrefix = import_picocolors3.default.green("+ ");
463
+ else if (node.status === "removed") diffPrefix = import_picocolors3.default.red("- ");
464
+ else if (node.status === "modified") diffPrefix = import_picocolors3.default.yellow("~ ");
465
+ lineContent = diffPrefix + indent + marker;
466
+ if (node.key) lineContent += formatKey(node.key) + import_picocolors3.default.dim(": ");
467
+ if (node.status === "added") {
468
+ lineContent += import_picocolors3.default.green(formatValue(node.newValue, 50));
469
+ } else if (node.status === "removed") {
470
+ lineContent += import_picocolors3.default.red(formatValue(node.oldValue, 50));
471
+ } else if (node.status === "modified") {
472
+ if (node.type === "object" || node.type === "array") {
473
+ lineContent += import_picocolors3.default.yellow(`{ ${node.itemCount} modified }`);
474
+ } else {
475
+ lineContent += `${import_picocolors3.default.red(formatValue(node.oldValue, 25))} -> ${import_picocolors3.default.green(formatValue(node.newValue, 25))}`;
476
+ }
477
+ } else {
478
+ lineContent += import_picocolors3.default.dim(formatValue(node.newValue, 50));
479
+ }
480
+ } else {
481
+ const treeNode = node;
482
+ lineContent = indent + marker;
483
+ if (treeNode.key) lineContent += formatKey(treeNode.key) + import_picocolors3.default.dim(": ");
484
+ if (treeNode.type === "object") {
485
+ lineContent += import_picocolors3.default.dim(`{ ${treeNode.itemCount} keys }`);
486
+ } else if (treeNode.type === "array") {
487
+ lineContent += import_picocolors3.default.dim(`[ ${treeNode.itemCount} items ]`);
488
+ } else {
489
+ lineContent += formatValue(treeNode.value, 60);
490
+ }
491
+ }
492
+ if (isSelected) {
493
+ console.log(import_picocolors3.default.bgBlue(import_picocolors3.default.white(import_picocolors3.default.bold(`> ${lineContent}`))));
494
+ } else {
495
+ console.log(` ${lineContent}`);
496
+ }
497
+ }
498
+ const selectedItem = this.flatList[this.selectedIndex];
499
+ const currentPath = selectedItem ? selectedItem.node.path : "$";
500
+ console.log("\n" + import_picocolors3.default.dim("\u2500".repeat(Math.min(process.stdout.columns || 80, 80))));
501
+ if (this.notification) {
502
+ console.log(import_picocolors3.default.green(import_picocolors3.default.bold(` ${this.notification}`)));
503
+ } else if (this.isSearching) {
504
+ console.log(import_picocolors3.default.yellow(import_picocolors3.default.bold(` Search: /${this.searchInput}\u2588`)));
505
+ } else {
506
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.cyan(` Path: ${currentPath}`)));
507
+ console.log(
508
+ import_picocolors3.default.dim(" [\u2191/\u2193] Move [Enter/Space] Toggle [e] Expand All [c] Collapse All [p] Copy Path [y] Copy Val [/] Search [q] Exit")
509
+ );
510
+ }
511
+ }
512
+ expandAll(node) {
513
+ node.collapsed = false;
514
+ if (node.children) node.children.forEach((c) => this.expandAll(c));
515
+ }
516
+ collapseAll(node) {
517
+ if (node.depth > 0) node.collapsed = true;
518
+ if (node.children) node.children.forEach((c) => this.collapseAll(c));
519
+ }
520
+ async start() {
521
+ if (!process.stdin.isTTY) {
522
+ return;
523
+ }
524
+ import_readline.default.emitKeypressEvents(process.stdin);
525
+ process.stdin.setRawMode(true);
526
+ process.stdin.resume();
527
+ this.render();
528
+ return new Promise((resolve) => {
529
+ const onKeypress = async (str, key) => {
530
+ if (this.isSearching) {
531
+ if (key.name === "return") {
532
+ this.searchQuery = this.searchInput;
533
+ this.isSearching = false;
534
+ this.rebuildFlatList();
535
+ this.render();
536
+ return;
537
+ }
538
+ if (key.name === "escape") {
539
+ this.isSearching = false;
540
+ this.searchInput = "";
541
+ this.render();
542
+ return;
543
+ }
544
+ if (key.name === "backspace") {
545
+ this.searchInput = this.searchInput.slice(0, -1);
546
+ this.render();
547
+ return;
548
+ }
549
+ if (str && str.length === 1) {
550
+ this.searchInput += str;
551
+ this.render();
552
+ return;
553
+ }
554
+ }
555
+ if (key.name === "up" || key.name === "k") {
556
+ if (this.selectedIndex > 0) {
557
+ this.selectedIndex--;
558
+ this.render();
559
+ }
560
+ } else if (key.name === "down" || key.name === "j") {
561
+ if (this.selectedIndex < this.flatList.length - 1) {
562
+ this.selectedIndex++;
563
+ this.render();
564
+ }
565
+ } else if (key.name === "return" || key.name === "space" || key.name === "right" || key.name === "left") {
566
+ const item = this.flatList[this.selectedIndex];
567
+ if (item && item.node.children && item.node.children.length > 0) {
568
+ item.node.collapsed = !item.node.collapsed;
569
+ this.rebuildFlatList();
570
+ this.render();
571
+ }
572
+ } else if (str === "e" || str === "E") {
573
+ this.expandAll(this.root);
574
+ this.rebuildFlatList();
575
+ this.render();
576
+ } else if (str === "c" || str === "C") {
577
+ this.collapseAll(this.root);
578
+ this.rebuildFlatList();
579
+ this.render();
580
+ } else if (str === "p" || str === "P") {
581
+ const item = this.flatList[this.selectedIndex];
582
+ if (item) {
583
+ await copyToClipboard(item.node.path);
584
+ this.setNotification(`\u2714 Copied JSONPath: ${item.node.path}`);
585
+ }
586
+ } else if (str === "y" || str === "Y") {
587
+ const item = this.flatList[this.selectedIndex];
588
+ if (item) {
589
+ const val = "value" in item.node ? item.node.value : item.node.newValue;
590
+ const textToCopy = typeof val === "object" ? JSON.stringify(val, null, 2) : String(val);
591
+ await copyToClipboard(textToCopy);
592
+ this.setNotification(`\u2714 Copied value to clipboard!`);
593
+ }
594
+ } else if (str === "/") {
595
+ this.isSearching = true;
596
+ this.searchInput = "";
597
+ this.render();
598
+ } else if (key.ctrl && key.name === "c" || key.name === "escape" || str === "q" || str === "Q") {
599
+ process.stdin.removeListener("keypress", onKeypress);
600
+ process.stdin.setRawMode(false);
601
+ process.stdin.pause();
602
+ process.stdout.write("\x1B[?25h\n");
603
+ resolve();
604
+ }
605
+ };
606
+ process.stdin.on("keypress", onKeypress);
607
+ });
608
+ }
609
+ };
610
+
611
+ // src/cli.ts
612
+ async function readStdin() {
613
+ return new Promise((resolve) => {
614
+ let data = "";
615
+ if (process.stdin.isTTY) {
616
+ resolve("");
617
+ return;
618
+ }
619
+ process.stdin.setEncoding("utf-8");
620
+ process.stdin.on("data", (chunk) => {
621
+ data += chunk;
622
+ });
623
+ process.stdin.on("end", () => {
624
+ resolve(data.trim());
625
+ });
626
+ setTimeout(() => {
627
+ if (!data) resolve("");
628
+ }, 100);
629
+ });
630
+ }
631
+ function parseJSON(raw, label = "Input") {
632
+ try {
633
+ return JSON.parse(raw);
634
+ } catch (err) {
635
+ throw new Error(`Invalid JSON in ${label}: ${err.message}`);
636
+ }
637
+ }
638
+ async function main() {
639
+ const program = new import_commander.Command();
640
+ program.name("json-term").description("High-fidelity interactive terminal JSON viewer, collapsible tree explorer, type syntax highlighter, and structural diff tool.").version("1.0.0", "-v, --version", "Output the current version").argument("[file]", "JSON file to view or compare").option("-p, --print", "Print formatted non-interactive tree to stdout", false).option("-d, --depth <number>", "Initial tree expansion depth", (val) => parseInt(val, 10), 2).option("-s, --search <query>", "Filter tree by key or value query").action(async (fileArg, options) => {
641
+ try {
642
+ let raw = "";
643
+ if (fileArg) {
644
+ if (!import_fs.default.existsSync(fileArg)) {
645
+ console.error(import_picocolors4.default.red(`Error: File not found: ${fileArg}`));
646
+ process.exit(1);
647
+ }
648
+ raw = await import_fs.default.promises.readFile(fileArg, "utf-8");
649
+ } else {
650
+ raw = await readStdin();
651
+ }
652
+ if (!raw) {
653
+ program.help();
654
+ process.exit(1);
655
+ }
656
+ const data = parseJSON(raw, fileArg || "stdin");
657
+ const tree = buildTree(data, fileArg || "root", "$", 0, options.depth);
658
+ if (options.print || !process.stdin.isTTY) {
659
+ console.log(prettyPrintTree(tree));
660
+ } else {
661
+ const viewer = new InteractiveViewer(tree, false);
662
+ await viewer.start();
663
+ }
664
+ } catch (err) {
665
+ console.error(import_picocolors4.default.red(`Error: ${err.message || err}`));
666
+ process.exit(1);
667
+ }
668
+ });
669
+ program.command("diff <fileA> [fileB]").description("Structural JSON diff between two files or stdin").option("-p, --print", "Print formatted non-interactive diff to stdout", false).option("-d, --depth <number>", "Initial expansion depth", (val) => parseInt(val, 10), 3).option("--no-unchanged", "Hide unchanged nodes from the diff view", false).action(async (fileA, fileB, diffOptions) => {
670
+ try {
671
+ if (!import_fs.default.existsSync(fileA)) {
672
+ console.error(import_picocolors4.default.red(`Error: File not found: ${fileA}`));
673
+ process.exit(1);
674
+ }
675
+ const rawA = await import_fs.default.promises.readFile(fileA, "utf-8");
676
+ const dataA = parseJSON(rawA, fileA);
677
+ let dataB;
678
+ if (fileB) {
679
+ if (!import_fs.default.existsSync(fileB)) {
680
+ console.error(import_picocolors4.default.red(`Error: File not found: ${fileB}`));
681
+ process.exit(1);
682
+ }
683
+ const rawB = await import_fs.default.promises.readFile(fileB, "utf-8");
684
+ dataB = parseJSON(rawB, fileB);
685
+ } else {
686
+ const stdinData = await readStdin();
687
+ if (!stdinData) {
688
+ console.error(import_picocolors4.default.red("Error: Please specify second file or pipe JSON via stdin."));
689
+ process.exit(1);
690
+ }
691
+ dataB = parseJSON(stdinData, "stdin");
692
+ }
693
+ const diffTree = diffJSON(dataA, dataB, `${fileA} \u2194 ${fileB || "stdin"}`, "$", 0, diffOptions.depth);
694
+ const stats = calculateDiffStats(diffTree);
695
+ if (diffOptions.print || !process.stdin.isTTY) {
696
+ console.log(
697
+ import_picocolors4.default.bold(
698
+ `Diff Stats: ${import_picocolors4.default.green(`+${stats.added}`)} ${import_picocolors4.default.red(`-${stats.removed}`)} ${import_picocolors4.default.yellow(`~${stats.modified}`)} ${import_picocolors4.default.dim(`=${stats.unchanged}`)}
699
+ `
700
+ )
701
+ );
702
+ console.log(prettyPrintDiff(diffTree, { showUnchanged: diffOptions.unchanged }));
703
+ } else {
704
+ const viewer = new InteractiveViewer(diffTree, true);
705
+ await viewer.start();
706
+ }
707
+ } catch (err) {
708
+ console.error(import_picocolors4.default.red(`Error: ${err.message || err}`));
709
+ process.exit(1);
710
+ }
711
+ });
712
+ await program.parseAsync(process.argv);
713
+ }
714
+ main().catch((err) => {
715
+ console.error(import_picocolors4.default.red(`Error: ${err.message || err}`));
716
+ process.exit(1);
717
+ });
718
+ //# sourceMappingURL=cli.js.map