120 lines
3.3 KiB
JavaScript
120 lines
3.3 KiB
JavaScript
import React, { useState, useCallback } from "react";
|
|
import { render, Box, Text, useApp, useInput } from "ink";
|
|
|
|
import MainMenu from "./screens/MainMenu.jsx";
|
|
import FrpMenu from "./screens/FrpMenu.jsx";
|
|
import TokenPrompt from "./screens/TokenPrompt.jsx";
|
|
import ProxyForm from "./screens/ProxyForm.jsx";
|
|
import ProxyList from "./screens/ProxyList.jsx";
|
|
import PlanPreview from "./screens/PlanPreview.jsx";
|
|
import RunLog from "./screens/RunLog.jsx";
|
|
import FrpConfigForm from "./screens/FrpConfigForm.jsx";
|
|
import SshKeyPrompt from "./screens/SshKeyPrompt.jsx";
|
|
|
|
function App() {
|
|
const { exit } = useApp();
|
|
const [stack, setStack] = useState([{ name: "main" }]);
|
|
const screen = stack[stack.length - 1];
|
|
|
|
const push = useCallback((next) => {
|
|
setStack((current) => [...current, next]);
|
|
}, []);
|
|
|
|
const replace = useCallback((next) => {
|
|
setStack((current) => [...current.slice(0, -1), next]);
|
|
}, []);
|
|
|
|
const back = useCallback(() => {
|
|
setStack((current) => (current.length > 1 ? current.slice(0, -1) : current));
|
|
}, []);
|
|
|
|
const home = useCallback(() => {
|
|
setStack([{ name: "main" }]);
|
|
}, []);
|
|
|
|
useInput((input, key) => {
|
|
if (key.escape && stack.length > 1 && screen.name !== "run") {
|
|
back();
|
|
}
|
|
if (input === "q" && screen.name === "main") {
|
|
exit();
|
|
}
|
|
});
|
|
|
|
const nav = { push, replace, back, home, exit };
|
|
|
|
return (
|
|
<Box flexDirection="column" paddingX={1}>
|
|
<Header />
|
|
<Box flexDirection="column" marginTop={1}>
|
|
{renderScreen(screen, nav)}
|
|
</Box>
|
|
<Footer screen={screen.name} />
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
function renderScreen(screen, nav) {
|
|
switch (screen.name) {
|
|
case "main":
|
|
return <MainMenu nav={nav} />;
|
|
case "frp":
|
|
return <FrpMenu nav={nav} />;
|
|
case "token":
|
|
return <TokenPrompt nav={nav} {...screen.props} />;
|
|
case "frp-config-form":
|
|
return <FrpConfigForm nav={nav} {...screen.props} />;
|
|
case "proxy-form":
|
|
return <ProxyForm nav={nav} {...screen.props} />;
|
|
case "proxy-list":
|
|
return <ProxyList nav={nav} {...screen.props} />;
|
|
case "ssh-key":
|
|
return <SshKeyPrompt nav={nav} {...screen.props} />;
|
|
case "plan":
|
|
return <PlanPreview nav={nav} {...screen.props} />;
|
|
case "run":
|
|
return <RunLog nav={nav} {...screen.props} />;
|
|
default:
|
|
return <Text>Unknown screen: {screen.name}</Text>;
|
|
}
|
|
}
|
|
|
|
const LOGO = String.raw` ____ ____ _ _
|
|
| _ \ ___ ___ _ __ | _ \ ___ | (_) ___ _ _
|
|
| | | |/ _ \/ _ \ '_ \| |_) / _ \| | |/ __| | | |
|
|
| |_| | __/ __/ |_) | __/ (_) | | | (__| |_| |
|
|
|____/ \___|\___| .__/|_| \___/|_|_|\___|\__, |
|
|
|_| |___/`;
|
|
|
|
function Header() {
|
|
return (
|
|
<Box flexDirection="column" borderStyle="round" borderColor="cyan" paddingX={1}>
|
|
<Text color="cyan" bold>{LOGO}</Text>
|
|
<Box marginTop={1}>
|
|
<Text color="cyan" bold>server-config</Text>
|
|
<Text dimColor> zsh · ssh · frp client</Text>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
function Footer({ screen }) {
|
|
const hint =
|
|
screen === "main"
|
|
? "↑↓ select · Enter confirm · q quit"
|
|
: screen === "run"
|
|
? "(Esc disabled while running)"
|
|
: "↑↓ select · Enter confirm · Esc back";
|
|
return (
|
|
<Box marginTop={1}>
|
|
<Text dimColor>{hint}</Text>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
export function start() {
|
|
render(<App />);
|
|
}
|
|
|
|
start();
|