arc402-cli 0.9.19 → 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 +41 -2
- package/dist/abis.d.ts +1 -0
- package/dist/abis.d.ts.map +1 -1
- package/dist/abis.js +45 -14
- package/dist/abis.js.map +1 -1
- package/dist/bundler.d.ts +1 -1
- package/dist/bundler.d.ts.map +1 -1
- package/dist/bundler.js +61 -27
- package/dist/bundler.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -5
- package/dist/client.js.map +1 -1
- package/dist/coinbase-smart-wallet.js +4 -1
- package/dist/coinbase-smart-wallet.js.map +1 -1
- package/dist/commands/accept.js +28 -25
- package/dist/commands/accept.js.map +1 -1
- package/dist/commands/agent-handshake.js +18 -15
- package/dist/commands/agent-handshake.js.map +1 -1
- package/dist/commands/agent.js +104 -98
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/agreements.js +98 -62
- package/dist/commands/agreements.js.map +1 -1
- package/dist/commands/arbitrator.js +81 -45
- package/dist/commands/arbitrator.js.map +1 -1
- package/dist/commands/arena-handshake.d.ts.map +1 -1
- package/dist/commands/arena-handshake.js +35 -53
- package/dist/commands/arena-handshake.js.map +1 -1
- package/dist/commands/arena.js +18 -12
- package/dist/commands/arena.js.map +1 -1
- package/dist/commands/backup.js +36 -30
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/cancel.js +18 -15
- package/dist/commands/cancel.js.map +1 -1
- package/dist/commands/channel.js +81 -45
- package/dist/commands/channel.js.map +1 -1
- package/dist/commands/coldstart.js +34 -31
- package/dist/commands/coldstart.js.map +1 -1
- package/dist/commands/compute.d.ts +14 -0
- package/dist/commands/compute.d.ts.map +1 -0
- package/dist/commands/compute.js +466 -0
- package/dist/commands/compute.js.map +1 -0
- package/dist/commands/config.js +30 -24
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/contract-interaction.js +15 -12
- package/dist/commands/contract-interaction.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +135 -98
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/deliver.js +76 -37
- package/dist/commands/deliver.js.map +1 -1
- package/dist/commands/discover.js +27 -24
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dispute.js +110 -104
- package/dist/commands/dispute.js.map +1 -1
- package/dist/commands/doctor.js +55 -16
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/endpoint.js +95 -56
- package/dist/commands/endpoint.js.map +1 -1
- package/dist/commands/feed.js +18 -11
- package/dist/commands/feed.js.map +1 -1
- package/dist/commands/hire.js +40 -37
- package/dist/commands/hire.js.map +1 -1
- package/dist/commands/migrate.js +33 -30
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/negotiate.d.ts.map +1 -1
- package/dist/commands/negotiate.js +36 -34
- package/dist/commands/negotiate.js.map +1 -1
- package/dist/commands/openshell.js +104 -68
- package/dist/commands/openshell.js.map +1 -1
- package/dist/commands/owner.js +20 -17
- package/dist/commands/owner.js.map +1 -1
- package/dist/commands/policy.js +43 -41
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/relay.d.ts.map +1 -1
- package/dist/commands/relay.js +51 -18
- package/dist/commands/relay.js.map +1 -1
- package/dist/commands/remediate.js +23 -20
- package/dist/commands/remediate.js.map +1 -1
- package/dist/commands/reputation.js +27 -25
- package/dist/commands/reputation.js.map +1 -1
- package/dist/commands/setup.js +104 -65
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/trust.js +20 -17
- package/dist/commands/trust.js.map +1 -1
- package/dist/commands/verify.js +21 -18
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/wallet.js +645 -679
- package/dist/commands/wallet.js.map +1 -1
- package/dist/commands/watch.js +36 -33
- package/dist/commands/watch.js.map +1 -1
- package/dist/commands/watchtower.js +73 -37
- package/dist/commands/watchtower.js.map +1 -1
- package/dist/commands/workroom.d.ts.map +1 -1
- package/dist/commands/workroom.js +282 -143
- package/dist/commands/workroom.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +71 -22
- package/dist/config.js.map +1 -1
- package/dist/daemon/compute-metering.d.ts +61 -0
- package/dist/daemon/compute-metering.d.ts.map +1 -0
- package/dist/daemon/compute-metering.js +299 -0
- package/dist/daemon/compute-metering.js.map +1 -0
- package/dist/daemon/compute-session.d.ts +100 -0
- package/dist/daemon/compute-session.d.ts.map +1 -0
- package/dist/daemon/compute-session.js +231 -0
- package/dist/daemon/compute-session.js.map +1 -0
- package/dist/daemon/config.d.ts +19 -1
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +90 -16
- package/dist/daemon/config.js.map +1 -1
- package/dist/daemon/credentials.d.ts +24 -0
- package/dist/daemon/credentials.d.ts.map +1 -0
- package/dist/daemon/credentials.js +80 -0
- package/dist/daemon/credentials.js.map +1 -0
- package/dist/daemon/delivery-client.d.ts +35 -0
- package/dist/daemon/delivery-client.d.ts.map +1 -0
- package/dist/daemon/delivery-client.js +231 -0
- package/dist/daemon/delivery-client.js.map +1 -0
- package/dist/daemon/file-delivery.d.ts +98 -0
- package/dist/daemon/file-delivery.d.ts.map +1 -0
- package/dist/daemon/file-delivery.js +461 -0
- package/dist/daemon/file-delivery.js.map +1 -0
- package/dist/daemon/hire-listener.d.ts +3 -3
- package/dist/daemon/hire-listener.d.ts.map +1 -1
- package/dist/daemon/hire-listener.js +47 -13
- package/dist/daemon/hire-listener.js.map +1 -1
- package/dist/daemon/index.d.ts +2 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +526 -53
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/job-lifecycle.d.ts +1 -1
- package/dist/daemon/job-lifecycle.d.ts.map +1 -1
- package/dist/daemon/job-lifecycle.js +51 -11
- package/dist/daemon/job-lifecycle.js.map +1 -1
- package/dist/daemon/notify.d.ts +1 -1
- package/dist/daemon/notify.d.ts.map +1 -1
- package/dist/daemon/notify.js +53 -19
- package/dist/daemon/notify.js.map +1 -1
- package/dist/daemon/token-metering.js +47 -8
- package/dist/daemon/token-metering.js.map +1 -1
- package/dist/daemon/userops.d.ts +2 -2
- package/dist/daemon/userops.d.ts.map +1 -1
- package/dist/daemon/userops.js +27 -23
- package/dist/daemon/userops.js.map +1 -1
- package/dist/daemon/wallet-monitor.d.ts +1 -1
- package/dist/daemon/wallet-monitor.d.ts.map +1 -1
- package/dist/daemon/wallet-monitor.js +12 -8
- package/dist/daemon/wallet-monitor.js.map +1 -1
- package/dist/daemon/worker-executor.d.ts +71 -0
- package/dist/daemon/worker-executor.d.ts.map +1 -0
- package/dist/daemon/worker-executor.js +382 -0
- package/dist/daemon/worker-executor.js.map +1 -0
- package/dist/drain-v4.js +64 -26
- package/dist/drain-v4.js.map +1 -1
- package/dist/endpoint-config.js +63 -20
- package/dist/endpoint-config.js.map +1 -1
- package/dist/endpoint-notify.d.ts.map +1 -1
- package/dist/endpoint-notify.js +49 -15
- package/dist/endpoint-notify.js.map +1 -1
- package/dist/index.js +50 -18
- package/dist/index.js.map +1 -1
- package/dist/openshell-runtime.d.ts.map +1 -1
- package/dist/openshell-runtime.js +82 -38
- package/dist/openshell-runtime.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +85 -78
- package/dist/program.js.map +1 -1
- package/dist/repl.js +31 -25
- package/dist/repl.js.map +1 -1
- package/dist/signing.js +6 -3
- package/dist/signing.js.map +1 -1
- package/dist/telegram-notify.js +40 -3
- package/dist/telegram-notify.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +56 -89
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Footer.js +7 -4
- package/dist/tui/Footer.js.map +1 -1
- package/dist/tui/Header.d.ts +1 -1
- package/dist/tui/Header.d.ts.map +1 -1
- package/dist/tui/Header.js +14 -9
- package/dist/tui/Header.js.map +1 -1
- package/dist/tui/InputLine.d.ts +2 -1
- package/dist/tui/InputLine.d.ts.map +1 -1
- package/dist/tui/InputLine.js +47 -97
- package/dist/tui/InputLine.js.map +1 -1
- package/dist/tui/Viewport.d.ts +1 -2
- package/dist/tui/Viewport.d.ts.map +1 -1
- package/dist/tui/Viewport.js +26 -6
- package/dist/tui/Viewport.js.map +1 -1
- package/dist/tui/WalletConnectPairing.js +19 -16
- package/dist/tui/WalletConnectPairing.js.map +1 -1
- package/dist/tui/components/Button.js +9 -6
- package/dist/tui/components/Button.js.map +1 -1
- package/dist/tui/components/CeremonyView.js +8 -5
- package/dist/tui/components/CeremonyView.js.map +1 -1
- package/dist/tui/components/CompletionDropdown.js +9 -6
- package/dist/tui/components/CompletionDropdown.js.map +1 -1
- package/dist/tui/components/ConfirmPrompt.js +8 -5
- package/dist/tui/components/ConfirmPrompt.js.map +1 -1
- package/dist/tui/components/CustomTextInput.js +14 -11
- package/dist/tui/components/CustomTextInput.js.map +1 -1
- package/dist/tui/components/InteractiveTable.js +12 -9
- package/dist/tui/components/InteractiveTable.js.map +1 -1
- package/dist/tui/components/StepSpinner.js +13 -10
- package/dist/tui/components/StepSpinner.js.map +1 -1
- package/dist/tui/components/Toast.js +12 -8
- package/dist/tui/components/Toast.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +21 -28
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/useChat.js +19 -13
- package/dist/tui/useChat.js.map +1 -1
- package/dist/tui/useCommand.d.ts +2 -3
- package/dist/tui/useCommand.d.ts.map +1 -1
- package/dist/tui/useCommand.js +24 -100
- package/dist/tui/useCommand.js.map +1 -1
- package/dist/tui/useNotifications.js +8 -5
- package/dist/tui/useNotifications.js.map +1 -1
- package/dist/tui/useScroll.d.ts.map +1 -1
- package/dist/tui/useScroll.js +12 -15
- package/dist/tui/useScroll.js.map +1 -1
- package/dist/ui/banner.d.ts +0 -12
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +19 -35
- package/dist/ui/banner.js.map +1 -1
- package/dist/ui/colors.js +19 -13
- package/dist/ui/colors.js.map +1 -1
- package/dist/ui/format.js +14 -6
- package/dist/ui/format.js.map +1 -1
- package/dist/ui/qr-render.js +11 -4
- package/dist/ui/qr-render.js.map +1 -1
- package/dist/ui/rpc-fallback.js +11 -6
- package/dist/ui/rpc-fallback.js.map +1 -1
- package/dist/ui/spinner.js +12 -6
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/tree.js +6 -3
- package/dist/ui/tree.js.map +1 -1
- package/dist/utils/format.js +41 -27
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/hash.js +42 -4
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/time.js +6 -2
- package/dist/utils/time.js.map +1 -1
- package/dist/wallet-router.d.ts +1 -1
- package/dist/wallet-router.d.ts.map +1 -1
- package/dist/wallet-router.js +19 -12
- package/dist/wallet-router.js.map +1 -1
- package/dist/walletconnect-session.d.ts +1 -1
- package/dist/walletconnect-session.d.ts.map +1 -1
- package/dist/walletconnect-session.js +11 -6
- package/dist/walletconnect-session.js.map +1 -1
- package/dist/walletconnect.d.ts +5 -6
- package/dist/walletconnect.d.ts.map +1 -1
- package/dist/walletconnect.js +35 -32
- package/dist/walletconnect.js.map +1 -1
- package/package.json +11 -10
- package/INK6-UX-SPEC.md +0 -446
- package/MIGRATION-SPEC.md +0 -108
- package/TUI-SPEC.md +0 -214
- package/scripts/authorize-machine-key.ts +0 -43
- package/scripts/drain-wallet.ts +0 -149
- package/scripts/execute-spend-only.ts +0 -81
- package/scripts/register-agent-userop.ts +0 -186
- package/src/abis.ts +0 -187
- package/src/bundler.ts +0 -235
- package/src/client.ts +0 -36
- package/src/coinbase-smart-wallet.ts +0 -51
- package/src/commands/accept.ts +0 -64
- package/src/commands/agent-handshake.ts +0 -72
- package/src/commands/agent.ts +0 -691
- package/src/commands/agreements.ts +0 -350
- package/src/commands/arbitrator.ts +0 -180
- package/src/commands/arena-handshake.ts +0 -274
- package/src/commands/arena.ts +0 -122
- package/src/commands/backup.ts +0 -117
- package/src/commands/cancel.ts +0 -35
- package/src/commands/channel.ts +0 -218
- package/src/commands/coldstart.ts +0 -165
- package/src/commands/config.ts +0 -68
- package/src/commands/contract-interaction.ts +0 -166
- package/src/commands/daemon.ts +0 -1054
- package/src/commands/deliver.ts +0 -148
- package/src/commands/discover.ts +0 -350
- package/src/commands/dispute.ts +0 -375
- package/src/commands/doctor.ts +0 -172
- package/src/commands/endpoint.ts +0 -620
- package/src/commands/feed.ts +0 -229
- package/src/commands/hire.ts +0 -245
- package/src/commands/migrate.ts +0 -177
- package/src/commands/negotiate.ts +0 -272
- package/src/commands/openshell.ts +0 -1055
- package/src/commands/owner.ts +0 -35
- package/src/commands/policy.ts +0 -263
- package/src/commands/relay.ts +0 -277
- package/src/commands/remediate.ts +0 -24
- package/src/commands/reputation.ts +0 -79
- package/src/commands/setup.ts +0 -343
- package/src/commands/trust.ts +0 -27
- package/src/commands/verify.ts +0 -91
- package/src/commands/wallet.ts +0 -3548
- package/src/commands/watch.ts +0 -220
- package/src/commands/watchtower.ts +0 -248
- package/src/commands/workroom.ts +0 -963
- package/src/config.ts +0 -220
- package/src/daemon/config.ts +0 -344
- package/src/daemon/hire-listener.ts +0 -226
- package/src/daemon/index.ts +0 -1089
- package/src/daemon/job-lifecycle.ts +0 -215
- package/src/daemon/notify.ts +0 -297
- package/src/daemon/token-metering.ts +0 -183
- package/src/daemon/userops.ts +0 -119
- package/src/daemon/wallet-monitor.ts +0 -90
- package/src/drain-v4.ts +0 -159
- package/src/endpoint-config.ts +0 -83
- package/src/endpoint-notify.ts +0 -134
- package/src/index.ts +0 -74
- package/src/openshell-runtime.ts +0 -281
- package/src/program.ts +0 -88
- package/src/repl.ts +0 -178
- package/src/signing.ts +0 -28
- package/src/telegram-notify.ts +0 -88
- package/src/tui/App.tsx +0 -263
- package/src/tui/Footer.tsx +0 -18
- package/src/tui/Header.tsx +0 -45
- package/src/tui/InputLine.tsx +0 -243
- package/src/tui/Viewport.tsx +0 -51
- package/src/tui/WalletConnectPairing.tsx +0 -114
- package/src/tui/components/Button.tsx +0 -38
- package/src/tui/components/CeremonyView.tsx +0 -39
- package/src/tui/components/CompletionDropdown.tsx +0 -56
- package/src/tui/components/ConfirmPrompt.tsx +0 -36
- package/src/tui/components/CustomTextInput.tsx +0 -132
- package/src/tui/components/InteractiveTable.tsx +0 -106
- package/src/tui/components/StepSpinner.tsx +0 -84
- package/src/tui/components/Toast.tsx +0 -59
- package/src/tui/index.tsx +0 -90
- package/src/tui/useChat.ts +0 -103
- package/src/tui/useCommand.ts +0 -238
- package/src/tui/useNotifications.ts +0 -28
- package/src/tui/useScroll.ts +0 -69
- package/src/ui/banner.ts +0 -78
- package/src/ui/colors.ts +0 -30
- package/src/ui/format.ts +0 -78
- package/src/ui/qr-render.ts +0 -92
- package/src/ui/rpc-fallback.ts +0 -59
- package/src/ui/spinner.ts +0 -56
- package/src/ui/tree.ts +0 -16
- package/src/utils/format.ts +0 -48
- package/src/utils/hash.ts +0 -5
- package/src/utils/time.ts +0 -15
- package/src/wallet-router.ts +0 -178
- package/src/walletconnect-session.ts +0 -27
- package/src/walletconnect.ts +0 -309
- package/test/time.test.js +0 -11
- package/tsconfig.json +0 -33
package/src/tui/App.tsx
DELETED
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import React, { useState, useCallback, useEffect, useRef } from "react";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
4
|
-
import { Header } from "./Header.js";
|
|
5
|
-
import { Viewport } from "./Viewport.js";
|
|
6
|
-
import { Footer } from "./Footer.js";
|
|
7
|
-
import { InputLine } from "./InputLine.js";
|
|
8
|
-
import { useCommand } from "./useCommand.js";
|
|
9
|
-
import { useChat } from "./useChat.js";
|
|
10
|
-
import { useScroll } from "./useScroll.js";
|
|
11
|
-
import { useNotifications } from "./useNotifications.js";
|
|
12
|
-
import { ToastContainer } from "./components/Toast.js";
|
|
13
|
-
import { createProgram } from "../program.js";
|
|
14
|
-
import chalk from "chalk";
|
|
15
|
-
|
|
16
|
-
const pkg = createRequire(import.meta.url)("../../package.json") as { version: string };
|
|
17
|
-
|
|
18
|
-
const BUILTIN_CMDS = ["help", "exit", "quit", "clear", "status"];
|
|
19
|
-
|
|
20
|
-
interface AppProps {
|
|
21
|
-
version: string;
|
|
22
|
-
network?: string;
|
|
23
|
-
wallet?: string;
|
|
24
|
-
balance?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Root TUI component — fixed header/footer with scrollable viewport.
|
|
29
|
-
*/
|
|
30
|
-
export function App({ version, network, wallet, balance }: AppProps) {
|
|
31
|
-
const { exit } = useApp();
|
|
32
|
-
|
|
33
|
-
const { stdout } = useStdout();
|
|
34
|
-
|
|
35
|
-
const [outputBuffer, setOutputBuffer] = useState<string[]>([
|
|
36
|
-
chalk.dim(" Type 'help' to see available commands"),
|
|
37
|
-
"",
|
|
38
|
-
]);
|
|
39
|
-
const [isProcessing, setIsProcessing] = useState(false);
|
|
40
|
-
|
|
41
|
-
const { execute, isRunning } = useCommand();
|
|
42
|
-
const { send, isSending } = useChat();
|
|
43
|
-
|
|
44
|
-
// Compute viewport height from actual fixed rows:
|
|
45
|
-
// Header: 6 (art) + 1 (blank) + 1 (subtitle) + 1 (separator) = 9
|
|
46
|
-
// + 1 status line if any status items present
|
|
47
|
-
// App chrome: 1 (top separator) + 1 (bottom separator) + 1 (input line) = 3
|
|
48
|
-
const rows = stdout?.rows ?? process.stdout.rows ?? 24;
|
|
49
|
-
const hasStatus = !!(network || wallet || balance);
|
|
50
|
-
const headerRows = 9 + (hasStatus ? 1 : 0);
|
|
51
|
-
const chromeRows = 3; // top separator + bottom separator + input line
|
|
52
|
-
const viewportHeight = Math.max(1, rows - headerRows - chromeRows);
|
|
53
|
-
|
|
54
|
-
const { scrollOffset, isAutoScroll, scrollUp, scrollDown, snapToBottom } =
|
|
55
|
-
useScroll(viewportHeight);
|
|
56
|
-
|
|
57
|
-
const { toasts, dismiss: dismissToast } = useNotifications();
|
|
58
|
-
|
|
59
|
-
// Get top-level command names for dispatch detection
|
|
60
|
-
const [topCmds] = useState<string[]>(() => {
|
|
61
|
-
try {
|
|
62
|
-
const prog = createProgram();
|
|
63
|
-
return prog.commands.map((cmd) => cmd.name());
|
|
64
|
-
} catch {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// ── Enhanced keyboard shortcuts ────────────────────────────────────────
|
|
70
|
-
useInput((input, key) => {
|
|
71
|
-
// Ctrl+L — clear viewport
|
|
72
|
-
if (key.ctrl && input === "l") {
|
|
73
|
-
setOutputBuffer([]);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
// Escape — cancel / clear (snap to bottom if scrolled)
|
|
77
|
-
if (key.escape) {
|
|
78
|
-
snapToBottom();
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// ── Batched line appender ─────────────────────────────────────────────────
|
|
84
|
-
// Accumulate lines in a ref and flush once per microtask so that rapid
|
|
85
|
-
// onLine() calls (stdout monkey-patch, child-process pipe) produce a
|
|
86
|
-
// single React state update instead of one re-render per line.
|
|
87
|
-
const pendingLines = useRef<string[]>([]);
|
|
88
|
-
const flushScheduled = useRef(false);
|
|
89
|
-
|
|
90
|
-
const appendLine = useCallback((line: string) => {
|
|
91
|
-
pendingLines.current.push(line);
|
|
92
|
-
if (!flushScheduled.current) {
|
|
93
|
-
flushScheduled.current = true;
|
|
94
|
-
queueMicrotask(() => {
|
|
95
|
-
const batch = pendingLines.current;
|
|
96
|
-
pendingLines.current = [];
|
|
97
|
-
flushScheduled.current = false;
|
|
98
|
-
if (batch.length > 0) {
|
|
99
|
-
setOutputBuffer((prev) => [...prev, ...batch]);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}, []);
|
|
104
|
-
|
|
105
|
-
const handleCommand = useCallback(
|
|
106
|
-
async (input: string): Promise<void> => {
|
|
107
|
-
// Echo command to viewport
|
|
108
|
-
appendLine(
|
|
109
|
-
chalk.cyanBright("◈") +
|
|
110
|
-
" " +
|
|
111
|
-
chalk.dim("arc402") +
|
|
112
|
-
" " +
|
|
113
|
-
chalk.white(">") +
|
|
114
|
-
" " +
|
|
115
|
-
chalk.white(input)
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
// Snap to bottom on new command
|
|
119
|
-
snapToBottom();
|
|
120
|
-
setIsProcessing(true);
|
|
121
|
-
|
|
122
|
-
const firstWord = input.split(/\s+/)[0];
|
|
123
|
-
const allKnown = [...BUILTIN_CMDS, ...topCmds];
|
|
124
|
-
|
|
125
|
-
// ── Built-in: exit/quit ────────────────────────────────────────────────
|
|
126
|
-
if (input === "exit" || input === "quit") {
|
|
127
|
-
appendLine(
|
|
128
|
-
" " + chalk.cyanBright("◈") + chalk.dim(" goodbye")
|
|
129
|
-
);
|
|
130
|
-
setIsProcessing(false);
|
|
131
|
-
setTimeout(() => exit(), 100);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ── Built-in: clear ────────────────────────────────────────────────────
|
|
136
|
-
if (input === "clear") {
|
|
137
|
-
setOutputBuffer([]);
|
|
138
|
-
setIsProcessing(false);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ── Built-in: help ─────────────────────────────────────────────────────
|
|
143
|
-
if (input === "help" || input === "/help") {
|
|
144
|
-
const lines: string[] = [];
|
|
145
|
-
try {
|
|
146
|
-
const prog = createProgram();
|
|
147
|
-
prog.exitOverride();
|
|
148
|
-
const helpText: string[] = [];
|
|
149
|
-
prog.configureOutput({
|
|
150
|
-
writeOut: (str) => helpText.push(str),
|
|
151
|
-
writeErr: (str) => helpText.push(str),
|
|
152
|
-
});
|
|
153
|
-
try {
|
|
154
|
-
await prog.parseAsync(["node", "arc402", "--help"]);
|
|
155
|
-
} catch {
|
|
156
|
-
/* commander throws after printing help */
|
|
157
|
-
}
|
|
158
|
-
for (const chunk of helpText) {
|
|
159
|
-
for (const l of chunk.split("\n")) lines.push(l);
|
|
160
|
-
}
|
|
161
|
-
} catch {
|
|
162
|
-
lines.push(chalk.red("Failed to load help"));
|
|
163
|
-
}
|
|
164
|
-
lines.push("");
|
|
165
|
-
lines.push(chalk.cyanBright("Chat"));
|
|
166
|
-
lines.push(
|
|
167
|
-
" " +
|
|
168
|
-
chalk.white("<message>") +
|
|
169
|
-
chalk.dim(" Send message to OpenClaw gateway")
|
|
170
|
-
);
|
|
171
|
-
lines.push(
|
|
172
|
-
" " +
|
|
173
|
-
chalk.white("/chat <message>") +
|
|
174
|
-
chalk.dim(" Explicitly route to chat")
|
|
175
|
-
);
|
|
176
|
-
lines.push(
|
|
177
|
-
chalk.dim(
|
|
178
|
-
" Gateway: http://localhost:19000 (openclaw gateway start)"
|
|
179
|
-
)
|
|
180
|
-
);
|
|
181
|
-
lines.push("");
|
|
182
|
-
for (const l of lines) appendLine(l);
|
|
183
|
-
setIsProcessing(false);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ── Built-in: status ───────────────────────────────────────────────────
|
|
188
|
-
if (input === "status") {
|
|
189
|
-
if (network)
|
|
190
|
-
appendLine(
|
|
191
|
-
` ${chalk.dim("Network")} ${chalk.white(network)}`
|
|
192
|
-
);
|
|
193
|
-
if (wallet)
|
|
194
|
-
appendLine(` ${chalk.dim("Wallet")} ${chalk.white(wallet)}`);
|
|
195
|
-
if (balance)
|
|
196
|
-
appendLine(` ${chalk.dim("Balance")} ${chalk.white(balance)}`);
|
|
197
|
-
if (!network && !wallet && !balance)
|
|
198
|
-
appendLine(chalk.dim(" No config found. Run 'config init' to get started."));
|
|
199
|
-
appendLine("");
|
|
200
|
-
setIsProcessing(false);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ── /chat prefix or unknown command → chat ─────────────────────────────
|
|
205
|
-
const isExplicitChat = input.startsWith("/chat ");
|
|
206
|
-
const isChatInput =
|
|
207
|
-
isExplicitChat || (!allKnown.includes(firstWord) && firstWord !== "");
|
|
208
|
-
|
|
209
|
-
if (isChatInput) {
|
|
210
|
-
const msg = isExplicitChat ? input.slice(6).trim() : input;
|
|
211
|
-
if (msg) {
|
|
212
|
-
await send(msg, appendLine);
|
|
213
|
-
}
|
|
214
|
-
appendLine("");
|
|
215
|
-
setIsProcessing(false);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// ── Dispatch to commander ──────────────────────────────────────────────
|
|
220
|
-
await execute(input, appendLine);
|
|
221
|
-
appendLine("");
|
|
222
|
-
setIsProcessing(false);
|
|
223
|
-
},
|
|
224
|
-
[appendLine, execute, send, snapToBottom, topCmds, network, wallet, balance, exit]
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const isDisabled = isProcessing || isRunning || isSending;
|
|
228
|
-
|
|
229
|
-
const cols = Math.min(stdout?.columns ?? process.stdout.columns ?? 80, 120);
|
|
230
|
-
|
|
231
|
-
return (
|
|
232
|
-
<Box flexDirection="column" height="100%">
|
|
233
|
-
{/* BANNER — memoized, participates in flex layout */}
|
|
234
|
-
<Header version={version} network={network} wallet={wallet} balance={balance} />
|
|
235
|
-
|
|
236
|
-
{/* Separator */}
|
|
237
|
-
<Box flexShrink={0}>
|
|
238
|
-
<Text dimColor>{"─".repeat(cols)}</Text>
|
|
239
|
-
</Box>
|
|
240
|
-
|
|
241
|
-
{/* VIEWPORT — fills remaining space */}
|
|
242
|
-
<Viewport
|
|
243
|
-
lines={outputBuffer}
|
|
244
|
-
scrollOffset={scrollOffset}
|
|
245
|
-
isAutoScroll={isAutoScroll}
|
|
246
|
-
height={viewportHeight}
|
|
247
|
-
/>
|
|
248
|
-
|
|
249
|
-
{/* Notification toasts — above footer */}
|
|
250
|
-
<ToastContainer toasts={toasts} onDismiss={dismissToast} />
|
|
251
|
-
|
|
252
|
-
{/* Bottom separator */}
|
|
253
|
-
<Box flexShrink={0}>
|
|
254
|
-
<Text dimColor>{"─".repeat(cols)}</Text>
|
|
255
|
-
</Box>
|
|
256
|
-
|
|
257
|
-
{/* FOOTER — fixed, input pinned */}
|
|
258
|
-
<Footer>
|
|
259
|
-
<InputLine onSubmit={handleCommand} isDisabled={isDisabled} />
|
|
260
|
-
</Footer>
|
|
261
|
-
</Box>
|
|
262
|
-
);
|
|
263
|
-
}
|
package/src/tui/Footer.tsx
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box } from "ink";
|
|
3
|
-
|
|
4
|
-
interface FooterProps {
|
|
5
|
-
children: React.ReactNode;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Fixed footer containing the input line.
|
|
10
|
-
* Pinned at the bottom of the screen.
|
|
11
|
-
*/
|
|
12
|
-
export function Footer({ children }: FooterProps) {
|
|
13
|
-
return (
|
|
14
|
-
<Box flexShrink={0}>
|
|
15
|
-
{children}
|
|
16
|
-
</Box>
|
|
17
|
-
);
|
|
18
|
-
}
|
package/src/tui/Header.tsx
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box, Text } from "ink";
|
|
3
|
-
import { getBannerArt, getStatusItems } from "../ui/banner.js";
|
|
4
|
-
import type { BannerConfig } from "../ui/banner.js";
|
|
5
|
-
|
|
6
|
-
interface HeaderProps {
|
|
7
|
-
version: string;
|
|
8
|
-
network?: string;
|
|
9
|
-
wallet?: string;
|
|
10
|
-
balance?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Fixed header showing the ASCII art banner + status info.
|
|
15
|
-
* Status items use flexWrap to adapt to narrow terminals.
|
|
16
|
-
*/
|
|
17
|
-
export const Header = React.memo(function Header({
|
|
18
|
-
network,
|
|
19
|
-
wallet,
|
|
20
|
-
balance,
|
|
21
|
-
}: HeaderProps) {
|
|
22
|
-
const { artLines, subtitle, separator } = getBannerArt();
|
|
23
|
-
const statusItems = getStatusItems({ network, wallet, balance });
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<Box flexDirection="column" flexShrink={0}>
|
|
27
|
-
{artLines.map((line, i) => (
|
|
28
|
-
<Text key={i}>{line}</Text>
|
|
29
|
-
))}
|
|
30
|
-
<Text> </Text>
|
|
31
|
-
<Text>{subtitle}</Text>
|
|
32
|
-
<Text>{separator}</Text>
|
|
33
|
-
{statusItems.length > 0 && (
|
|
34
|
-
<Box flexWrap="wrap" columnGap={2}>
|
|
35
|
-
{statusItems.map((item) => (
|
|
36
|
-
<Box key={item.label}>
|
|
37
|
-
<Text dimColor>{item.label}</Text>
|
|
38
|
-
<Text>{" "}{item.value}</Text>
|
|
39
|
-
</Box>
|
|
40
|
-
))}
|
|
41
|
-
</Box>
|
|
42
|
-
)}
|
|
43
|
-
</Box>
|
|
44
|
-
);
|
|
45
|
-
});
|
package/src/tui/InputLine.tsx
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import React, { useState, useCallback, useMemo } from "react";
|
|
2
|
-
import { Box, Text, useInput } from "ink";
|
|
3
|
-
import { CustomTextInput } from "./components/CustomTextInput.js";
|
|
4
|
-
import { createProgram } from "../program.js";
|
|
5
|
-
import { CompletionDropdown } from "./components/CompletionDropdown.js";
|
|
6
|
-
|
|
7
|
-
const BUILTIN_CMDS = ["help", "exit", "quit", "clear", "status"];
|
|
8
|
-
|
|
9
|
-
interface InputLineProps {
|
|
10
|
-
onSubmit: (value: string) => void;
|
|
11
|
-
isDisabled?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Input line with command history navigation, tab completion, and dropdown.
|
|
16
|
-
*/
|
|
17
|
-
export function InputLine({ onSubmit, isDisabled = false }: InputLineProps) {
|
|
18
|
-
const [value, setValue] = useState("");
|
|
19
|
-
const [history, setHistory] = useState<string[]>([]);
|
|
20
|
-
const [historyIdx, setHistoryIdx] = useState(-1);
|
|
21
|
-
const [historyTemp, setHistoryTemp] = useState("");
|
|
22
|
-
|
|
23
|
-
// Dropdown state
|
|
24
|
-
const [showDropdown, setShowDropdown] = useState(false);
|
|
25
|
-
const [dropdownIdx, setDropdownIdx] = useState(0);
|
|
26
|
-
const [dropdownCandidates, setDropdownCandidates] = useState<string[]>([]);
|
|
27
|
-
|
|
28
|
-
// Lazily build command list for tab completion
|
|
29
|
-
const [topCmds] = useState<string[]>(() => {
|
|
30
|
-
try {
|
|
31
|
-
const prog = createProgram();
|
|
32
|
-
return prog.commands.map((cmd) => cmd.name());
|
|
33
|
-
} catch {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
const [subCmds] = useState<Map<string, string[]>>(() => {
|
|
38
|
-
try {
|
|
39
|
-
const prog = createProgram();
|
|
40
|
-
const map = new Map<string, string[]>();
|
|
41
|
-
for (const cmd of prog.commands) {
|
|
42
|
-
if (cmd.commands.length > 0) {
|
|
43
|
-
map.set(cmd.name(), cmd.commands.map((s) => s.name()));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return map;
|
|
47
|
-
} catch {
|
|
48
|
-
return new Map();
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const getCompletions = useCallback(
|
|
53
|
-
(input: string): string[] => {
|
|
54
|
-
const allTop = [...BUILTIN_CMDS, ...topCmds];
|
|
55
|
-
const trimmed = input.trimStart();
|
|
56
|
-
const spaceIdx = trimmed.indexOf(" ");
|
|
57
|
-
|
|
58
|
-
if (spaceIdx === -1) {
|
|
59
|
-
return allTop.filter((cmd) => cmd.startsWith(trimmed));
|
|
60
|
-
}
|
|
61
|
-
const parent = trimmed.slice(0, spaceIdx);
|
|
62
|
-
const rest = trimmed.slice(spaceIdx + 1);
|
|
63
|
-
const subs = subCmds.get(parent) ?? [];
|
|
64
|
-
return subs
|
|
65
|
-
.filter((s) => s.startsWith(rest))
|
|
66
|
-
.map((s) => `${parent} ${s}`);
|
|
67
|
-
},
|
|
68
|
-
[topCmds, subCmds]
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
const handleSubmit = useCallback(
|
|
72
|
-
(val: string) => {
|
|
73
|
-
const trimmed = val.trim();
|
|
74
|
-
if (!trimmed) return;
|
|
75
|
-
|
|
76
|
-
setHistory((prev) => {
|
|
77
|
-
if (prev[prev.length - 1] === trimmed) return prev;
|
|
78
|
-
return [...prev, trimmed];
|
|
79
|
-
});
|
|
80
|
-
setHistoryIdx(-1);
|
|
81
|
-
setHistoryTemp("");
|
|
82
|
-
setValue("");
|
|
83
|
-
setShowDropdown(false);
|
|
84
|
-
|
|
85
|
-
onSubmit(trimmed);
|
|
86
|
-
},
|
|
87
|
-
[onSubmit]
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
useInput(
|
|
91
|
-
(_input, key) => {
|
|
92
|
-
if (isDisabled) return;
|
|
93
|
-
|
|
94
|
-
// ── Dropdown navigation ───────────────────────────────────────────
|
|
95
|
-
if (showDropdown) {
|
|
96
|
-
if (key.upArrow) {
|
|
97
|
-
setDropdownIdx((i) => Math.max(0, i - 1));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (key.downArrow) {
|
|
101
|
-
setDropdownIdx((i) =>
|
|
102
|
-
Math.min(dropdownCandidates.length - 1, i + 1)
|
|
103
|
-
);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (key.return) {
|
|
107
|
-
// Select the highlighted candidate
|
|
108
|
-
if (dropdownCandidates[dropdownIdx]) {
|
|
109
|
-
setValue(dropdownCandidates[dropdownIdx] + " ");
|
|
110
|
-
}
|
|
111
|
-
setShowDropdown(false);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (key.escape) {
|
|
115
|
-
setShowDropdown(false);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ── History navigation ────────────────────────────────────────────
|
|
121
|
-
if (key.upArrow) {
|
|
122
|
-
setHistory((hist) => {
|
|
123
|
-
setHistoryIdx((idx) => {
|
|
124
|
-
if (idx === -1) {
|
|
125
|
-
setHistoryTemp(value);
|
|
126
|
-
const newIdx = hist.length - 1;
|
|
127
|
-
if (newIdx >= 0) setValue(hist[newIdx]);
|
|
128
|
-
return newIdx;
|
|
129
|
-
} else if (idx > 0) {
|
|
130
|
-
const newIdx = idx - 1;
|
|
131
|
-
setValue(hist[newIdx]);
|
|
132
|
-
return newIdx;
|
|
133
|
-
}
|
|
134
|
-
return idx;
|
|
135
|
-
});
|
|
136
|
-
return hist;
|
|
137
|
-
});
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (key.downArrow) {
|
|
142
|
-
setHistory((hist) => {
|
|
143
|
-
setHistoryIdx((idx) => {
|
|
144
|
-
if (idx >= 0) {
|
|
145
|
-
const newIdx = idx + 1;
|
|
146
|
-
if (newIdx >= hist.length) {
|
|
147
|
-
setValue(historyTemp);
|
|
148
|
-
return -1;
|
|
149
|
-
} else {
|
|
150
|
-
setValue(hist[newIdx]);
|
|
151
|
-
return newIdx;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return idx;
|
|
155
|
-
});
|
|
156
|
-
return hist;
|
|
157
|
-
});
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// ── Tab — completion / dropdown ───────────────────────────────────
|
|
162
|
-
if (key.tab) {
|
|
163
|
-
const completions = getCompletions(value);
|
|
164
|
-
|
|
165
|
-
if (completions.length === 0) {
|
|
166
|
-
setShowDropdown(false);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (completions.length === 1) {
|
|
171
|
-
setValue(completions[0] + " ");
|
|
172
|
-
setShowDropdown(false);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Show dropdown with multiple candidates
|
|
177
|
-
setDropdownCandidates(completions);
|
|
178
|
-
setDropdownIdx(0);
|
|
179
|
-
setShowDropdown(true);
|
|
180
|
-
|
|
181
|
-
// Also advance to common prefix
|
|
182
|
-
const common = completions.reduce((a, b) => {
|
|
183
|
-
let i = 0;
|
|
184
|
-
while (i < a.length && i < b.length && a[i] === b[i]) i++;
|
|
185
|
-
return a.slice(0, i);
|
|
186
|
-
});
|
|
187
|
-
if (common.length > value.trimStart().length) {
|
|
188
|
-
setValue(common);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// ── Escape — dismiss dropdown ─────────────────────────────────────
|
|
193
|
-
if (key.escape) {
|
|
194
|
-
setShowDropdown(false);
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
{ isActive: !isDisabled }
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// Auto-show completions as user types (no Tab needed)
|
|
201
|
-
const handleChange = useCallback(
|
|
202
|
-
(newVal: string) => {
|
|
203
|
-
setValue(newVal);
|
|
204
|
-
const trimmed = newVal.trimStart();
|
|
205
|
-
if (trimmed.length >= 2) {
|
|
206
|
-
const completions = getCompletions(newVal);
|
|
207
|
-
if (completions.length > 1) {
|
|
208
|
-
setDropdownCandidates(completions);
|
|
209
|
-
setDropdownIdx(0);
|
|
210
|
-
setShowDropdown(true);
|
|
211
|
-
} else {
|
|
212
|
-
setShowDropdown(false);
|
|
213
|
-
}
|
|
214
|
-
} else {
|
|
215
|
-
setShowDropdown(false);
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
[getCompletions]
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
return (
|
|
222
|
-
<Box flexDirection="column">
|
|
223
|
-
{/* Completion dropdown renders above the input */}
|
|
224
|
-
<CompletionDropdown
|
|
225
|
-
candidates={dropdownCandidates}
|
|
226
|
-
selectedIndex={dropdownIdx}
|
|
227
|
-
visible={showDropdown}
|
|
228
|
-
/>
|
|
229
|
-
<Box>
|
|
230
|
-
<Text color="cyan">◈</Text>
|
|
231
|
-
<Text dimColor> arc402 </Text>
|
|
232
|
-
<Text color="white">{">"} </Text>
|
|
233
|
-
<CustomTextInput
|
|
234
|
-
value={value}
|
|
235
|
-
onChange={handleChange}
|
|
236
|
-
onSubmit={handleSubmit}
|
|
237
|
-
focus={!isDisabled}
|
|
238
|
-
suppressEnter={showDropdown}
|
|
239
|
-
/>
|
|
240
|
-
</Box>
|
|
241
|
-
</Box>
|
|
242
|
-
);
|
|
243
|
-
}
|
package/src/tui/Viewport.tsx
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box, Text } from "ink";
|
|
3
|
-
|
|
4
|
-
interface ViewportProps {
|
|
5
|
-
lines: string[];
|
|
6
|
-
scrollOffset: number;
|
|
7
|
-
isAutoScroll: boolean;
|
|
8
|
-
height: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Scrollable output area that fills remaining terminal space.
|
|
13
|
-
* Renders a window slice of the buffer, not terminal scroll.
|
|
14
|
-
* scrollOffset=0 means pinned to bottom (auto-scroll).
|
|
15
|
-
* Positive scrollOffset means scrolled up by that many lines.
|
|
16
|
-
*/
|
|
17
|
-
export function Viewport({ lines, scrollOffset, isAutoScroll, height }: ViewportProps) {
|
|
18
|
-
const viewportHeight = Math.max(1, height);
|
|
19
|
-
|
|
20
|
-
// Compute the window slice
|
|
21
|
-
const totalLines = lines.length;
|
|
22
|
-
let endIdx: number;
|
|
23
|
-
let startIdx: number;
|
|
24
|
-
|
|
25
|
-
if (scrollOffset === 0) {
|
|
26
|
-
endIdx = totalLines;
|
|
27
|
-
startIdx = Math.max(0, endIdx - viewportHeight);
|
|
28
|
-
} else {
|
|
29
|
-
endIdx = Math.max(0, totalLines - scrollOffset);
|
|
30
|
-
startIdx = Math.max(0, endIdx - viewportHeight);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const visibleLines = lines.slice(startIdx, endIdx);
|
|
34
|
-
|
|
35
|
-
const canScrollUp = startIdx > 0;
|
|
36
|
-
const canScrollDown = scrollOffset > 0;
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<Box flexDirection="column" flexGrow={1}>
|
|
40
|
-
{canScrollUp && (
|
|
41
|
-
<Text dimColor>{" \u2191 more (Shift+Up)"}</Text>
|
|
42
|
-
)}
|
|
43
|
-
{visibleLines.map((line, i) => (
|
|
44
|
-
<Text key={startIdx + i}>{line}</Text>
|
|
45
|
-
))}
|
|
46
|
-
{canScrollDown && !isAutoScroll && (
|
|
47
|
-
<Text dimColor>{" \u2193 more (Shift+Down / Esc)"}</Text>
|
|
48
|
-
)}
|
|
49
|
-
</Box>
|
|
50
|
-
);
|
|
51
|
-
}
|