@olenbetong/appframe-cli 4.3.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/cli/af-apply.js +1 -1
- package/cli/af-deploy.js +1 -1
- package/cli/editor/TransactionsEditor.js +134 -158
- package/cli/editor/TransactionsPreviewDialog.js +175 -104
- package/cli/editor/tableFormatting.js +9 -80
- package/cli/editor/useCommand.js +148 -0
- package/cli/editor/useScrollableText.js +28 -0
- package/cli/editor/useTransactionEdits.js +142 -0
- package/cli/editor/useTransactionText.js +113 -0
- package/cli/editor/useTransactions.js +53 -0
- package/cli/editor/useTransactionsSelection.js +41 -0
- package/cli/editor/useTransactionsTableLayout.js +54 -0
- package/cli/editor/{useTransactionsTableViewport.js → useVirtualScrolling.js} +62 -24
- package/cli/editor/useVirtualText.js +18 -0
- package/package.json +2 -2
- package/src/af-apply.ts +1 -1
- package/src/af-deploy.ts +1 -1
- package/src/editor/TransactionsEditor.tsx +155 -180
- package/src/editor/TransactionsPreviewDialog.tsx +196 -146
- package/src/editor/tableFormatting.ts +8 -94
- package/src/editor/useCommand.ts +191 -0
- package/src/editor/useScrollableText.ts +48 -0
- package/src/editor/useTransactionEdits.ts +183 -0
- package/src/editor/useTransactionText.ts +153 -0
- package/src/editor/useTransactions.ts +75 -0
- package/src/editor/useTransactionsSelection.ts +54 -0
- package/src/editor/useTransactionsTableLayout.ts +84 -0
- package/src/editor/{useTransactionsTableViewport.ts → useVirtualScrolling.ts} +87 -38
- package/src/editor/useVirtualText.ts +32 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/cli/editor/useTransactionsEditorData.js +0 -206
- package/cli/editor/useTransactionsEditorInput.js +0 -109
- package/src/editor/useTransactionsEditorData.ts +0 -245
- package/src/editor/useTransactionsEditorInput.ts +0 -147
|
@@ -1,116 +1,178 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { transactionStatusLabels } from "@olenbetong/appframe-updater";
|
|
4
|
+
import { Box, Text } from "ink";
|
|
5
|
+
import { useMemo } from "react";
|
|
5
6
|
import { FullScreenBox } from "./FullScreenBox.js";
|
|
7
|
+
import { STATUS_DIRTY_INDICATOR } from "./tableFormatting.js";
|
|
8
|
+
import { useCommand } from "./useCommand.js";
|
|
6
9
|
import { useScreenSize } from "./useScreenSize.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
if (value === null) {
|
|
22
|
-
return ["null"];
|
|
10
|
+
import { useScrollableText } from "./useScrollableText.js";
|
|
11
|
+
import { useTransactionEdits } from "./useTransactionEdits.js";
|
|
12
|
+
import { useTransactions } from "./useTransactions.js";
|
|
13
|
+
import { useTransactionText } from "./useTransactionText.js";
|
|
14
|
+
import { useVirtualText } from "./useVirtualText.js";
|
|
15
|
+
const PREVIEW_SAVE_SHORTCUTS = ["ctrl+s", "meta+s"];
|
|
16
|
+
const NO_TRANSACTION_FILTER = "1 = 0";
|
|
17
|
+
function escapePrimKey(primKey) {
|
|
18
|
+
return primKey.replace(/'/g, "''");
|
|
19
|
+
}
|
|
20
|
+
function createPrimKeyFilter(primKey) {
|
|
21
|
+
if (!primKey) {
|
|
22
|
+
return NO_TRANSACTION_FILTER;
|
|
23
23
|
}
|
|
24
|
-
return
|
|
24
|
+
return `PrimKey = '${escapePrimKey(primKey)}'`;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
function Shortcut({ keyboard, command }) {
|
|
27
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
28
|
+
"[",
|
|
29
|
+
keyboard,
|
|
30
|
+
"] ",
|
|
31
|
+
command
|
|
32
|
+
] });
|
|
33
|
+
}
|
|
34
|
+
export function TransactionsPreviewDialog({
|
|
35
|
+
server,
|
|
36
|
+
namespace,
|
|
37
|
+
frameWidth,
|
|
38
|
+
focusedPrimKey,
|
|
39
|
+
focusNext,
|
|
40
|
+
focusPrevious
|
|
41
|
+
}) {
|
|
42
|
+
let transactionFilter = useMemo(() => createPrimKeyFilter(focusedPrimKey), [focusedPrimKey]);
|
|
43
|
+
let { loading, error, refresh, normalizedTransactions, transactionMap } = useTransactions({
|
|
44
|
+
server,
|
|
45
|
+
namespace,
|
|
46
|
+
filter: transactionFilter
|
|
47
|
+
});
|
|
48
|
+
let selectedPrimKeys = useMemo(
|
|
49
|
+
() => focusedPrimKey ? /* @__PURE__ */ new Set([focusedPrimKey]) : /* @__PURE__ */ new Set(),
|
|
50
|
+
[focusedPrimKey]
|
|
34
51
|
);
|
|
35
|
-
let
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
let { pendingStatuses, cycleStatus, save } = useTransactionEdits({
|
|
53
|
+
normalizedTransactions,
|
|
54
|
+
transactionRecordMap: transactionMap,
|
|
55
|
+
selectedPrimKeys,
|
|
56
|
+
server,
|
|
57
|
+
refresh
|
|
58
|
+
});
|
|
59
|
+
let record = focusedPrimKey ? transactionMap.get(focusedPrimKey) : void 0;
|
|
60
|
+
let pendingStatus = focusedPrimKey ? pendingStatuses[focusedPrimKey] : void 0;
|
|
61
|
+
let statusPreviewValue = useMemo(() => {
|
|
62
|
+
if (!record) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
let originalStatus = record.Status;
|
|
66
|
+
let numericOriginal = typeof originalStatus === "number" ? originalStatus : typeof originalStatus === "string" ? Number(originalStatus) : null;
|
|
67
|
+
let baseText;
|
|
68
|
+
if (pendingStatus !== void 0) {
|
|
69
|
+
baseText = String(pendingStatus);
|
|
70
|
+
} else if (originalStatus === null) {
|
|
71
|
+
baseText = "null";
|
|
72
|
+
} else if (originalStatus === void 0) {
|
|
73
|
+
baseText = "";
|
|
74
|
+
} else {
|
|
75
|
+
baseText = String(originalStatus);
|
|
38
76
|
}
|
|
39
|
-
let
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
})
|
|
49
|
-
});
|
|
50
|
-
if (entryIndex < previewEntries.length - 1) {
|
|
51
|
-
items.push({ type: "spacer", key: `${entry.key}-spacer` });
|
|
77
|
+
let labelSource = null;
|
|
78
|
+
if (pendingStatus !== void 0) {
|
|
79
|
+
labelSource = pendingStatus;
|
|
80
|
+
} else if (numericOriginal !== null && Number.isFinite(numericOriginal)) {
|
|
81
|
+
labelSource = numericOriginal;
|
|
82
|
+
}
|
|
83
|
+
if (labelSource !== null) {
|
|
84
|
+
let label = transactionStatusLabels[labelSource];
|
|
85
|
+
if (label) {
|
|
86
|
+
baseText = baseText.length > 0 ? `${baseText} (${label})` : `(${label})`;
|
|
52
87
|
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
88
|
+
}
|
|
89
|
+
let isDirty = false;
|
|
90
|
+
if (pendingStatus !== void 0) {
|
|
91
|
+
isDirty = numericOriginal === null || !Number.isFinite(numericOriginal) || pendingStatus !== numericOriginal;
|
|
92
|
+
}
|
|
93
|
+
if (isDirty) {
|
|
94
|
+
baseText = `${baseText}${STATUS_DIRTY_INDICATOR}`;
|
|
95
|
+
}
|
|
96
|
+
return baseText;
|
|
97
|
+
}, [pendingStatus, record]);
|
|
98
|
+
let { text: transactionText, dialogWidth } = useTransactionText({
|
|
99
|
+
transaction: record,
|
|
100
|
+
statusPreviewValue,
|
|
101
|
+
frameWidth
|
|
102
|
+
});
|
|
60
103
|
let { height: screenHeight } = useScreenSize();
|
|
61
|
-
let headerLineCount = 3 + (record
|
|
104
|
+
let headerLineCount = 3 + (record?.Name ? 1 : 0);
|
|
62
105
|
let marginLines = 1;
|
|
63
|
-
let
|
|
64
|
-
let contentLineCount =
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
106
|
+
let baseNonContentLines = headerLineCount + marginLines;
|
|
107
|
+
let contentLineCount = transactionText.length > 0 ? transactionText.length : 1;
|
|
108
|
+
let computeLayout = (extraNonContentLines) => {
|
|
109
|
+
let nonContentLines = baseNonContentLines + extraNonContentLines;
|
|
110
|
+
let naturalDialogHeight = nonContentLines + contentLineCount;
|
|
111
|
+
let maxDialogHeight = Math.max(screenHeight - 4, nonContentLines + 1);
|
|
112
|
+
maxDialogHeight = Math.min(maxDialogHeight, Math.max(screenHeight - 2, 1));
|
|
113
|
+
let dialogHeightValue = Math.min(naturalDialogHeight, maxDialogHeight);
|
|
114
|
+
let contentHeightValue = Math.max(dialogHeightValue - nonContentLines, 1);
|
|
115
|
+
return { dialogHeight: dialogHeightValue, contentHeight: contentHeightValue, nonContentLines };
|
|
116
|
+
};
|
|
117
|
+
let { dialogHeight, contentHeight } = computeLayout(0);
|
|
118
|
+
let showScrollSummary = record && transactionText.length > contentHeight;
|
|
119
|
+
if (showScrollSummary) {
|
|
120
|
+
({ dialogHeight, contentHeight } = computeLayout(2));
|
|
121
|
+
}
|
|
71
122
|
let previewSignature = useMemo(
|
|
72
|
-
() =>
|
|
73
|
-
[
|
|
123
|
+
() => `${focusedPrimKey ?? ""}|${transactionText.join("|")}`,
|
|
124
|
+
[focusedPrimKey, transactionText]
|
|
74
125
|
);
|
|
75
|
-
let
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
if (key.downArrow) {
|
|
94
|
-
setScrollOffset((offset) => Math.min(offset + 1, maxScrollOffset));
|
|
95
|
-
return;
|
|
126
|
+
let { scrollOffset } = useScrollableText({
|
|
127
|
+
textLength: transactionText.length,
|
|
128
|
+
viewportHeight: contentHeight,
|
|
129
|
+
signature: previewSignature
|
|
130
|
+
});
|
|
131
|
+
let { visibleText, totalLines, offset } = useVirtualText({
|
|
132
|
+
text: transactionText,
|
|
133
|
+
viewportHeight: contentHeight,
|
|
134
|
+
scrollOffset
|
|
135
|
+
});
|
|
136
|
+
useCommand("left", focusPrevious, true);
|
|
137
|
+
useCommand("right", focusNext, true);
|
|
138
|
+
useCommand(
|
|
139
|
+
PREVIEW_SAVE_SHORTCUTS,
|
|
140
|
+
() => {
|
|
141
|
+
if (focusedPrimKey) {
|
|
142
|
+
save({ primKey: focusedPrimKey });
|
|
96
143
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
144
|
+
},
|
|
145
|
+
focusedPrimKey !== null
|
|
146
|
+
);
|
|
147
|
+
useCommand(
|
|
148
|
+
"s",
|
|
149
|
+
() => {
|
|
150
|
+
if (focusedPrimKey) {
|
|
151
|
+
cycleStatus(focusedPrimKey, { exclusive: true });
|
|
104
152
|
}
|
|
105
153
|
},
|
|
106
|
-
|
|
154
|
+
focusedPrimKey !== null
|
|
107
155
|
);
|
|
108
|
-
let visibleItems = previewItems.slice(scrollOffset, scrollOffset + contentHeight);
|
|
109
156
|
let summaryText = useMemo(() => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
157
|
+
if (totalLines === 0) {
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
let start = offset + 1;
|
|
161
|
+
let end = Math.min(offset + contentHeight, totalLines);
|
|
162
|
+
return `Showing ${start}\u2013${end} of ${totalLines}`;
|
|
163
|
+
}, [contentHeight, offset, totalLines]);
|
|
164
|
+
let bodyContent;
|
|
165
|
+
if (!focusedPrimKey) {
|
|
166
|
+
bodyContent = /* @__PURE__ */ jsx(Text, { color: "gray", children: "No transaction selected." });
|
|
167
|
+
} else if (error) {
|
|
168
|
+
bodyContent = /* @__PURE__ */ jsx(Text, { color: "red", children: error.message });
|
|
169
|
+
} else if (!record) {
|
|
170
|
+
bodyContent = loading ? /* @__PURE__ */ jsx(Text, { color: "gray", children: "Loading transaction\u2026" }) : /* @__PURE__ */ jsx(Text, { color: "yellow", children: "Transaction not found." });
|
|
171
|
+
} else if (transactionText.length === 0) {
|
|
172
|
+
bodyContent = /* @__PURE__ */ jsx(Text, { color: "gray", children: "No data available." });
|
|
173
|
+
} else {
|
|
174
|
+
bodyContent = visibleText.map((line, index) => /* @__PURE__ */ jsx(Text, { children: line.length > 0 ? line : " " }, `${offset + index}`));
|
|
175
|
+
}
|
|
114
176
|
return /* @__PURE__ */ jsx(FullScreenBox, { position: "absolute", justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsxs(
|
|
115
177
|
Box,
|
|
116
178
|
{
|
|
@@ -123,15 +185,24 @@ export function TransactionsPreviewDialog({ record, frameWidth }) {
|
|
|
123
185
|
backgroundColor: "black",
|
|
124
186
|
children: [
|
|
125
187
|
/* @__PURE__ */ jsx(Text, { bold: true, children: "Transaction preview" }),
|
|
126
|
-
record
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
188
|
+
record?.Name ? /* @__PURE__ */ jsx(Text, { children: record.Name }) : null,
|
|
189
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
190
|
+
/* @__PURE__ */ jsx(Shortcut, { keyboard: "esc", command: "Close preview" }),
|
|
191
|
+
" \xB7 ",
|
|
192
|
+
/* @__PURE__ */ jsx(Shortcut, { keyboard: "\u2190/\u2192", command: "Change focus" }),
|
|
193
|
+
" \xB7",
|
|
194
|
+
" ",
|
|
195
|
+
/* @__PURE__ */ jsx(Shortcut, { keyboard: "\u2191/\u2193", command: "Scroll" }),
|
|
196
|
+
" \xB7 ",
|
|
197
|
+
/* @__PURE__ */ jsx(Shortcut, { keyboard: "PgUp/PgDn", command: "Scroll faster" }),
|
|
198
|
+
" \xB7",
|
|
199
|
+
" ",
|
|
200
|
+
/* @__PURE__ */ jsx(Shortcut, { keyboard: "s", command: "Cycle status" }),
|
|
201
|
+
" \xB7 ",
|
|
202
|
+
/* @__PURE__ */ jsx(Shortcut, { keyboard: "Ctrl+S", command: "Save" })
|
|
203
|
+
] }),
|
|
204
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", height: contentHeight, children: bodyContent }),
|
|
205
|
+
showScrollSummary ? /* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx(Text, { color: "gray", children: summaryText }) }) : null
|
|
135
206
|
]
|
|
136
207
|
}
|
|
137
208
|
) });
|
|
@@ -9,7 +9,6 @@ const STATUS_COLUMN_MIN_WIDTH = (() => {
|
|
|
9
9
|
let longest = labels.reduce((max, label) => Math.max(max, label.length), 0);
|
|
10
10
|
return Math.max("Status".length, longest + STATUS_DIRTY_INDICATOR.length);
|
|
11
11
|
})();
|
|
12
|
-
export const ERROR_COLUMN_MAX_LINES = 5;
|
|
13
12
|
export function sanitizeCellValue(value) {
|
|
14
13
|
let s = String(value ?? "");
|
|
15
14
|
s = s.replace(/[\r\n]+/g, " ").replace(/\t/g, " ");
|
|
@@ -28,55 +27,6 @@ export function truncateCellValue(value, width) {
|
|
|
28
27
|
}
|
|
29
28
|
return `${value.slice(0, width - 1)}\u2026`;
|
|
30
29
|
}
|
|
31
|
-
export function wrapCellValue(value, width, maxLines) {
|
|
32
|
-
if (width <= 0 || maxLines <= 0) {
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
let sanitized = sanitizeCellValue(value);
|
|
36
|
-
if (sanitized.length === 0) {
|
|
37
|
-
return ["".padEnd(width, " ")];
|
|
38
|
-
}
|
|
39
|
-
let remaining = sanitized;
|
|
40
|
-
let lines = [];
|
|
41
|
-
while (remaining.length > 0 && lines.length < maxLines) {
|
|
42
|
-
if (remaining.length <= width) {
|
|
43
|
-
lines.push(remaining.padEnd(width, " "));
|
|
44
|
-
remaining = "";
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
let sliceEnd = Math.min(width, remaining.length);
|
|
48
|
-
let breakIndex = remaining.lastIndexOf(" ", sliceEnd);
|
|
49
|
-
if (breakIndex <= 0) {
|
|
50
|
-
breakIndex = sliceEnd;
|
|
51
|
-
}
|
|
52
|
-
let chunk = remaining.slice(0, breakIndex).trimEnd();
|
|
53
|
-
if (chunk.length === 0) {
|
|
54
|
-
chunk = remaining.slice(0, width);
|
|
55
|
-
breakIndex = chunk.length;
|
|
56
|
-
}
|
|
57
|
-
lines.push(chunk.padEnd(width, " "));
|
|
58
|
-
remaining = remaining.slice(breakIndex).trimStart();
|
|
59
|
-
}
|
|
60
|
-
if (remaining.length > 0) {
|
|
61
|
-
let ellipsisLine;
|
|
62
|
-
if (lines.length === 0) {
|
|
63
|
-
let truncated = remaining.slice(0, Math.max(width - 1, 0));
|
|
64
|
-
ellipsisLine = `${truncated}${width > 0 ? "\u2026" : ""}`;
|
|
65
|
-
} else {
|
|
66
|
-
let last = lines.pop() ?? "";
|
|
67
|
-
let trimmed = last.trimEnd();
|
|
68
|
-
if (trimmed.length >= width) {
|
|
69
|
-
trimmed = trimmed.slice(0, Math.max(width - 1, 0));
|
|
70
|
-
}
|
|
71
|
-
ellipsisLine = `${trimmed}${width > 0 ? "\u2026" : ""}`;
|
|
72
|
-
}
|
|
73
|
-
lines.push(ellipsisLine.padEnd(width, " "));
|
|
74
|
-
}
|
|
75
|
-
if (lines.length === 0) {
|
|
76
|
-
lines.push("".padEnd(width, " "));
|
|
77
|
-
}
|
|
78
|
-
return lines;
|
|
79
|
-
}
|
|
80
30
|
export function calculateColumnWidths({
|
|
81
31
|
columns,
|
|
82
32
|
rows,
|
|
@@ -156,35 +106,16 @@ export function formatRowLines({
|
|
|
156
106
|
widths,
|
|
157
107
|
separator = DEFAULT_TABLE_SEPARATOR
|
|
158
108
|
}) {
|
|
159
|
-
let
|
|
109
|
+
let parts = columns.map((column, index) => {
|
|
160
110
|
let width = widths[index];
|
|
161
|
-
if (column.wrap) {
|
|
162
|
-
let maxLines = column.maxWrapLines ?? 1;
|
|
163
|
-
return wrapCellValue(String(row[column.key] ?? ""), width, maxLines);
|
|
164
|
-
}
|
|
165
111
|
let value = sanitizeCellValue(row[column.key]);
|
|
166
|
-
return
|
|
112
|
+
return truncateCellValue(value, width);
|
|
167
113
|
});
|
|
168
|
-
let
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
let parts = columnLines.map((column, index) => {
|
|
172
|
-
let width = widths[index];
|
|
173
|
-
let value = column[lineIndex] ?? "";
|
|
174
|
-
if (value.length < width) {
|
|
175
|
-
return value.padEnd(width, " ");
|
|
176
|
-
}
|
|
177
|
-
if (value.length > width) {
|
|
178
|
-
return truncateCellValue(value, width);
|
|
179
|
-
}
|
|
180
|
-
return value;
|
|
181
|
-
});
|
|
182
|
-
lines.push(parts.join(separator));
|
|
183
|
-
}
|
|
184
|
-
if (lines.length === 0) {
|
|
185
|
-
lines.push(columns.map((_, index) => "".padEnd(widths[index], " ")).join(separator));
|
|
114
|
+
let line = parts.join(separator);
|
|
115
|
+
if (line.length === 0) {
|
|
116
|
+
return [columns.map((_, index) => "".padEnd(widths[index], " ")).join(separator)];
|
|
186
117
|
}
|
|
187
|
-
return
|
|
118
|
+
return [line];
|
|
188
119
|
}
|
|
189
120
|
export function addViewportEllipsis(line) {
|
|
190
121
|
if (line.length === 0) {
|
|
@@ -233,8 +164,8 @@ export function createStatusLine(message, stats, lineWidth, prefix = "") {
|
|
|
233
164
|
export function createTransactionColumns(showError) {
|
|
234
165
|
let columns = [
|
|
235
166
|
{ key: "Namespace", header: "Namespace", min: 10, max: 24, weight: 0, shrinkPriority: 3 },
|
|
236
|
-
{ key: "Name", header: "Name", min:
|
|
237
|
-
{ key: "CreatedBy", header: "CreatedBy", min:
|
|
167
|
+
{ key: "Name", header: "Name", min: 32, max: 40, weight: 1, shrinkPriority: 1 },
|
|
168
|
+
{ key: "CreatedBy", header: "CreatedBy", min: 13, max: 20, weight: 0, shrinkPriority: 4 },
|
|
238
169
|
{
|
|
239
170
|
key: "Status",
|
|
240
171
|
header: "Status",
|
|
@@ -251,9 +182,7 @@ export function createTransactionColumns(showError) {
|
|
|
251
182
|
min: 20,
|
|
252
183
|
max: 120,
|
|
253
184
|
weight: 2,
|
|
254
|
-
shrinkPriority: 0
|
|
255
|
-
wrap: true,
|
|
256
|
-
maxWrapLines: ERROR_COLUMN_MAX_LINES
|
|
185
|
+
shrinkPriority: 0
|
|
257
186
|
});
|
|
258
187
|
}
|
|
259
188
|
return columns;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import { useInput } from "ink";
|
|
3
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
4
|
+
const HOME_SEQUENCES = /* @__PURE__ */ new Set(["\x1B[H", "\x1B[1~", "\x1BOH"]);
|
|
5
|
+
const END_SEQUENCES = /* @__PURE__ */ new Set(["\x1B[F", "\x1B[4~", "\x1BOF"]);
|
|
6
|
+
const PAGE_UP_SEQUENCES = /* @__PURE__ */ new Set(["\x1B[5~"]);
|
|
7
|
+
const PAGE_DOWN_SEQUENCES = /* @__PURE__ */ new Set(["\x1B[6~"]);
|
|
8
|
+
const KEY_ALIASES = {
|
|
9
|
+
leftarrow: "left",
|
|
10
|
+
rightarrow: "right",
|
|
11
|
+
uparrow: "up",
|
|
12
|
+
downarrow: "down",
|
|
13
|
+
return: "enter",
|
|
14
|
+
enter: "enter",
|
|
15
|
+
esc: "escape",
|
|
16
|
+
spacebar: "space",
|
|
17
|
+
pagedown: "pagedown",
|
|
18
|
+
pageup: "pageup"
|
|
19
|
+
};
|
|
20
|
+
const RELAXED_KEYS = /* @__PURE__ */ new Set([
|
|
21
|
+
"escape",
|
|
22
|
+
"left",
|
|
23
|
+
"right",
|
|
24
|
+
"up",
|
|
25
|
+
"down",
|
|
26
|
+
"home",
|
|
27
|
+
"end",
|
|
28
|
+
"pageup",
|
|
29
|
+
"pagedown",
|
|
30
|
+
"tab",
|
|
31
|
+
"enter",
|
|
32
|
+
"delete",
|
|
33
|
+
"backspace"
|
|
34
|
+
]);
|
|
35
|
+
function parseShortcut(shortcut) {
|
|
36
|
+
let tokens = shortcut.trim().toLowerCase().split("+").map((token) => token.trim()).filter(Boolean);
|
|
37
|
+
let definition = {
|
|
38
|
+
key: null,
|
|
39
|
+
ctrl: false,
|
|
40
|
+
meta: false,
|
|
41
|
+
shift: false
|
|
42
|
+
};
|
|
43
|
+
for (let token of tokens) {
|
|
44
|
+
if (token === "ctrl" || token === "control") {
|
|
45
|
+
definition.ctrl = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (token === "meta" || token === "cmd" || token === "command") {
|
|
49
|
+
definition.meta = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (token === "shift") {
|
|
53
|
+
definition.shift = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
let alias = KEY_ALIASES[token];
|
|
57
|
+
definition.key = alias ?? token;
|
|
58
|
+
}
|
|
59
|
+
return definition;
|
|
60
|
+
}
|
|
61
|
+
function getInputKey(input, key) {
|
|
62
|
+
if (key.escape) {
|
|
63
|
+
return "escape";
|
|
64
|
+
}
|
|
65
|
+
if (key.leftArrow) {
|
|
66
|
+
return "left";
|
|
67
|
+
}
|
|
68
|
+
if (key.rightArrow) {
|
|
69
|
+
return "right";
|
|
70
|
+
}
|
|
71
|
+
if (key.upArrow) {
|
|
72
|
+
return "up";
|
|
73
|
+
}
|
|
74
|
+
if (key.downArrow) {
|
|
75
|
+
return "down";
|
|
76
|
+
}
|
|
77
|
+
if (key.pageUp || PAGE_UP_SEQUENCES.has(input)) {
|
|
78
|
+
return "pageup";
|
|
79
|
+
}
|
|
80
|
+
if (key.pageDown || PAGE_DOWN_SEQUENCES.has(input)) {
|
|
81
|
+
return "pagedown";
|
|
82
|
+
}
|
|
83
|
+
if (key.return) {
|
|
84
|
+
return "enter";
|
|
85
|
+
}
|
|
86
|
+
if (key.delete) {
|
|
87
|
+
return "delete";
|
|
88
|
+
}
|
|
89
|
+
if (key.backspace) {
|
|
90
|
+
return "backspace";
|
|
91
|
+
}
|
|
92
|
+
if (key.tab) {
|
|
93
|
+
return "tab";
|
|
94
|
+
}
|
|
95
|
+
if (key.shift && !input) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (input === " ") {
|
|
99
|
+
return "space";
|
|
100
|
+
}
|
|
101
|
+
if (HOME_SEQUENCES.has(input) || (key.home ?? false)) {
|
|
102
|
+
return "home";
|
|
103
|
+
}
|
|
104
|
+
if (END_SEQUENCES.has(input) || (key.end ?? false)) {
|
|
105
|
+
return "end";
|
|
106
|
+
}
|
|
107
|
+
if (input) {
|
|
108
|
+
return input.toLowerCase();
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function matchesShortcut(definition, input, key) {
|
|
113
|
+
let actualKey = getInputKey(input, key);
|
|
114
|
+
let relaxed = actualKey !== null && RELAXED_KEYS.has(actualKey);
|
|
115
|
+
if (definition.ctrl && !key.ctrl) return false;
|
|
116
|
+
if (definition.meta && !key.meta) return false;
|
|
117
|
+
if (definition.shift && !key.shift) return false;
|
|
118
|
+
if (!relaxed) {
|
|
119
|
+
if (!definition.ctrl && key.ctrl) return false;
|
|
120
|
+
if (!definition.meta && key.meta) return false;
|
|
121
|
+
if (!definition.shift && key.shift) return false;
|
|
122
|
+
}
|
|
123
|
+
if (definition.key === null) {
|
|
124
|
+
return actualKey !== null;
|
|
125
|
+
}
|
|
126
|
+
return actualKey === definition.key;
|
|
127
|
+
}
|
|
128
|
+
function normalizeShortcuts(shortcuts) {
|
|
129
|
+
let entries = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
|
|
130
|
+
return entries.map(parseShortcut);
|
|
131
|
+
}
|
|
132
|
+
export function useCommand(shortcut, handler, enabled = true) {
|
|
133
|
+
let definitions = useMemo(() => normalizeShortcuts(shortcut), [shortcut]);
|
|
134
|
+
let callback = useRef(handler);
|
|
135
|
+
callback.current = handler;
|
|
136
|
+
let inputHandler = useCallback(
|
|
137
|
+
(input, key) => {
|
|
138
|
+
for (let definition of definitions) {
|
|
139
|
+
if (matchesShortcut(definition, input, key)) {
|
|
140
|
+
callback.current();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
[definitions]
|
|
146
|
+
);
|
|
147
|
+
useInput(inputHandler, { isActive: enabled });
|
|
148
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useCommand } from "./useCommand.js";
|
|
4
|
+
export function useScrollableText({
|
|
5
|
+
textLength,
|
|
6
|
+
viewportHeight,
|
|
7
|
+
signature
|
|
8
|
+
}) {
|
|
9
|
+
let [scrollOffset, setScrollOffset] = useState(0);
|
|
10
|
+
let lastSignatureRef = useRef(signature);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (lastSignatureRef.current !== signature) {
|
|
13
|
+
lastSignatureRef.current = signature;
|
|
14
|
+
setScrollOffset(0);
|
|
15
|
+
}
|
|
16
|
+
}, [signature]);
|
|
17
|
+
let maxScrollOffset = useMemo(() => Math.max((textLength || 1) - viewportHeight, 0), [textLength, viewportHeight]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
setScrollOffset((offset) => Math.min(offset, maxScrollOffset));
|
|
20
|
+
}, [maxScrollOffset]);
|
|
21
|
+
let canScroll = textLength > viewportHeight && viewportHeight > 0;
|
|
22
|
+
let pageStep = useMemo(() => Math.max(viewportHeight - 1, 1), [viewportHeight]);
|
|
23
|
+
useCommand("up", () => setScrollOffset((offset) => Math.max(offset - 1, 0)), canScroll);
|
|
24
|
+
useCommand("down", () => setScrollOffset((offset) => Math.min(offset + 1, maxScrollOffset)), canScroll);
|
|
25
|
+
useCommand("pageup", () => setScrollOffset((offset) => Math.max(offset - pageStep, 0)), canScroll);
|
|
26
|
+
useCommand("pagedown", () => setScrollOffset((offset) => Math.min(offset + pageStep, maxScrollOffset)), canScroll);
|
|
27
|
+
return { scrollOffset, canScroll, maxScrollOffset, pageStep };
|
|
28
|
+
}
|