Skip to content

Commit 63efe2e

Browse files
committed
feat: 基本完善TUI为一个完整demo
1 parent 102c1f1 commit 63efe2e

30 files changed

Lines changed: 927 additions & 150 deletions

packages/tui/dist/app.js

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,54 @@
1-
import React from 'react';
2-
import { Text } from 'ink';
3-
export default function App({
4-
name = 'Stranger'
5-
}) {
6-
return /*#__PURE__*/React.createElement(Text, null, "Hello, ", /*#__PURE__*/React.createElement(Text, {
7-
color: "green"
8-
}, name));
1+
import React, { useState } from 'react';
2+
import { Box, useInput } from 'ink';
3+
import SearchBar from './components/SearchBar.js';
4+
import Sidebar from './components/Sidebar.js';
5+
import Content from './components/Content.js';
6+
import Footer from './components/Footer.js';
7+
import { menus } from './components/menuData.js';
8+
export default function App() {
9+
const [path, setPath] = useState('/home');
10+
const [focusId, setFocusId] = useState('sidebar'); // 'sidebar' | 'search' | 'content'
11+
12+
useInput((input, key) => {
13+
if (key.tab) {
14+
// Tab to switch focus
15+
setFocusId(prev => {
16+
if (prev === 'sidebar') return 'content';
17+
if (prev === 'content') return 'footer';
18+
if (prev === 'footer') return 'search';
19+
return 'sidebar';
20+
});
21+
}
22+
});
23+
const items = menus;
24+
return /*#__PURE__*/React.createElement(Box, {
25+
flexDirection: "column",
26+
padding: 1
27+
}, /*#__PURE__*/React.createElement(SearchBar, {
28+
isFocused: focusId === 'search',
29+
onSubmit: q => {
30+
setPath(`/search?q=${encodeURIComponent(q)}`);
31+
setFocusId('sidebar'); // return focus to sidebar
32+
}
33+
}), /*#__PURE__*/React.createElement(Box, {
34+
marginTop: 1,
35+
flexDirection: "column"
36+
}, /*#__PURE__*/React.createElement(Box, {
37+
flexDirection: "row"
38+
}, /*#__PURE__*/React.createElement(Sidebar, {
39+
items: items,
40+
isFocused: focusId === 'sidebar',
41+
onSelect: p => setPath(p)
42+
}), /*#__PURE__*/React.createElement(Box, {
43+
marginLeft: 2,
44+
flexGrow: 1,
45+
flexDirection: "column"
46+
}, /*#__PURE__*/React.createElement(Content, {
47+
path: path,
48+
isFocused: focusId === 'content'
49+
}))), /*#__PURE__*/React.createElement(Footer, {
50+
version: process.env.npm_package_version || 'dev',
51+
stars: 0,
52+
isFocused: focusId === 'footer'
53+
})));
954
}

packages/tui/dist/cli.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ const cli = meow(`
77
Usage
88
$ tui
99
10-
Options
11-
--name Your name
10+
Description
11+
DevSidecar CLI Dashboard
1212
13-
Examples
14-
$ tui --name=Jane
15-
Hello, Jane
13+
Controls
14+
Use arrow keys to navigate.
15+
Use Enter to select.
16+
Use Tab to switch focus between Sidebar, Search and Content.
1617
`, {
1718
importMeta: import.meta
1819
});
19-
render(/*#__PURE__*/React.createElement(App, {
20-
name: cli.flags.name
21-
}));
20+
render(/*#__PURE__*/React.createElement(App, null));
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react';
2+
import { Box } from 'ink';
3+
import IndexPage from '../pages/IndexPage.js';
4+
import HelpPage from '../pages/HelpPage.js';
5+
import PluginPage from '../pages/PluginPage.js';
6+
import ProxyPage from '../pages/ProxyPage.js';
7+
import ServerPage from '../pages/ServerPage.js';
8+
import SettingPage from '../pages/SettingPage.js';
9+
import { theme } from '../style/theme.js';
10+
export default function Content({
11+
path,
12+
isFocused = false
13+
}) {
14+
const renderByPath = () => {
15+
if (!path) return /*#__PURE__*/React.createElement(IndexPage, {
16+
isFocused: isFocused
17+
});
18+
if (path.startsWith('/home')) return /*#__PURE__*/React.createElement(IndexPage, {
19+
isFocused: isFocused
20+
});
21+
if (path.startsWith('/help')) return /*#__PURE__*/React.createElement(HelpPage, null);
22+
if (path.startsWith('/plugin')) return /*#__PURE__*/React.createElement(PluginPage, null);
23+
if (path.startsWith('/proxy')) return /*#__PURE__*/React.createElement(ProxyPage, null);
24+
if (path.startsWith('/server')) return /*#__PURE__*/React.createElement(ServerPage, null);
25+
if (path.startsWith('/settings')) return /*#__PURE__*/React.createElement(SettingPage, null);
26+
if (path.startsWith('/search')) return /*#__PURE__*/React.createElement(IndexPage, {
27+
isFocused: isFocused
28+
});
29+
return /*#__PURE__*/React.createElement(IndexPage, {
30+
isFocused: isFocused
31+
});
32+
};
33+
return /*#__PURE__*/React.createElement(Box, {
34+
paddingLeft: theme.spacing.padding,
35+
flexDirection: "column",
36+
flexGrow: 1,
37+
borderStyle: isFocused ? 'round' : 'single',
38+
borderColor: isFocused ? theme.colors.accent : 'gray'
39+
}, renderByPath());
40+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React, { useState } from 'react';
2+
import { Box, Text, useInput } from 'ink';
3+
import open from 'open';
4+
import Link from 'ink-link';
5+
import { theme } from '../style/theme.js';
6+
export default function Footer({
7+
version = 'dev',
8+
stars = 0,
9+
isFocused = false
10+
}) {
11+
const [selectedIndex, setSelectedIndex] = useState(0);
12+
const links = [{
13+
label: 'Greper',
14+
url: 'https://github.com/greper'
15+
}, {
16+
label: 'WangLiang',
17+
url: 'https://github.com/wangliang181230'
18+
}, {
19+
label: 'CuteOmega',
20+
url: 'https://github.com/cute-omega'
21+
}, {
22+
label: 'Project Home',
23+
url: 'https://github.com/docmirror/dev-sidecar'
24+
}];
25+
useInput((input, key) => {
26+
if (!isFocused) return;
27+
if (key.leftArrow) {
28+
setSelectedIndex(prev => Math.max(0, prev - 1));
29+
}
30+
if (key.rightArrow) {
31+
setSelectedIndex(prev => Math.min(links.length - 1, prev + 1));
32+
}
33+
if (key.return) {
34+
const link = links[selectedIndex];
35+
if (link) {
36+
open(link.url);
37+
}
38+
}
39+
});
40+
return /*#__PURE__*/React.createElement(Box, {
41+
flexDirection: "column",
42+
borderStyle: "single",
43+
borderTop: true,
44+
borderBottom: false,
45+
borderLeft: false,
46+
borderRight: false,
47+
borderColor: isFocused ? theme.colors.accent : 'gray',
48+
paddingTop: 1,
49+
marginTop: 1
50+
}, /*#__PURE__*/React.createElement(Box, {
51+
justifyContent: "space-between"
52+
}, /*#__PURE__*/React.createElement(Text, {
53+
color: theme.colors.muted
54+
}, "\xA92020-2026 docmirror.cn \u2014 ", version), /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Text, {
55+
color: theme.colors.muted
56+
}, "Authors: "), links.slice(0, 3).map((link, index) => /*#__PURE__*/React.createElement(Box, {
57+
key: link.label,
58+
marginRight: 1
59+
}, index > 0 && /*#__PURE__*/React.createElement(Text, null, ", "), /*#__PURE__*/React.createElement(Link, {
60+
url: link.url
61+
}, /*#__PURE__*/React.createElement(Text, {
62+
backgroundColor: selectedIndex === index && isFocused ? theme.colors.accent : undefined,
63+
color: selectedIndex === index && isFocused ? theme.colors.background : theme.colors.text,
64+
bold: selectedIndex === index && isFocused,
65+
underline: selectedIndex === index && isFocused
66+
}, link.label)))))), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement(Box, {
67+
marginTop: 1,
68+
justifyContent: "flex-end"
69+
}, /*#__PURE__*/React.createElement(Text, null, "\u5982\u679C\u5B83\u89E3\u51B3\u4E86\u4F60\u7684\u95EE\u9898\uFF0C\u8BF7\u4E0D\u8981\u541D\u556C\u4F60\u7684star\u54DF\uFF01\u70B9\u8FD9\u91CC", '->'), /*#__PURE__*/React.createElement(Box, {
70+
marginLeft: 1
71+
}, /*#__PURE__*/React.createElement(Link, {
72+
url: links[3].url
73+
}, /*#__PURE__*/React.createElement(Text, {
74+
backgroundColor: selectedIndex === 3 && isFocused ? theme.colors.accent : undefined,
75+
color: selectedIndex === 3 && isFocused ? theme.colors.background : theme.colors.logo,
76+
bold: selectedIndex === 3 && isFocused,
77+
underline: selectedIndex === 3 && isFocused
78+
}, "[Stars: ", stars, "]")))), isFocused && /*#__PURE__*/React.createElement(Text, {
79+
color: "gray",
80+
dimColor: true
81+
}, "(Use Left/Right to select, Enter to open)"));
82+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React, { useState } from 'react';
2+
import { Box, Text } from 'ink';
3+
import TextInput from 'ink-text-input';
4+
import { theme } from '../style/theme.js';
5+
export default function SearchBar({
6+
onSubmit,
7+
isFocused = false
8+
}) {
9+
const [value, setValue] = useState('');
10+
return /*#__PURE__*/React.createElement(Box, {
11+
marginBottom: 1,
12+
borderStyle: isFocused ? 'round' : 'single',
13+
borderColor: isFocused ? theme.colors.accent : 'gray',
14+
paddingX: 1
15+
}, /*#__PURE__*/React.createElement(Text, {
16+
color: theme.colors.muted,
17+
backgroundColor: isFocused ? theme.colors.accent : undefined
18+
}, "\u641C\u7D22:", ' '), /*#__PURE__*/React.createElement(TextInput, {
19+
value: value,
20+
onChange: setValue,
21+
focus: isFocused
22+
}));
23+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2+
import React from 'react';
3+
import { Box, Text } from 'ink';
4+
import SelectInput from 'ink-select-input';
5+
import { theme } from '../style/theme.js';
6+
export default function Sidebar({
7+
items = [],
8+
onSelect,
9+
isFocused = true
10+
}) {
11+
const handleSelect = item => {
12+
onSelect && onSelect(item.value);
13+
};
14+
return /*#__PURE__*/React.createElement(Box, {
15+
flexDirection: "column",
16+
width: theme.spacing.sidebarWidth,
17+
paddingX: 1,
18+
borderStyle: "single",
19+
borderRight: true,
20+
borderLeft: false,
21+
borderTop: false,
22+
borderBottom: false,
23+
borderColor: isFocused ? theme.colors.accent : 'gray'
24+
}, /*#__PURE__*/React.createElement(Box, _extends({
25+
marginBottom: 1,
26+
borderStyle: "single",
27+
borderBottom: true,
28+
borderTop: false,
29+
borderLeft: false,
30+
borderRight: false,
31+
borderColor: isFocused ? theme.colors.accent : 'gray'
32+
}, isFocused ? {
33+
backgroundColor: theme.colors.accent
34+
} : {}), /*#__PURE__*/React.createElement(Text, {
35+
color: theme.colors.logo,
36+
bold: true
37+
}, "Dev Sidecar - \u5F00\u53D1\u8005\u8FB9\u8F66")), /*#__PURE__*/React.createElement(SelectInput, {
38+
items: items,
39+
onSelect: handleSelect,
40+
isFocused: isFocused
41+
}));
42+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const menus = [{
2+
label: '主页',
3+
value: '/home'
4+
}, {
5+
label: '插件',
6+
value: '/plugin'
7+
}, {
8+
label: '设置',
9+
value: '/settings'
10+
}, {
11+
label: '帮助',
12+
value: '/help'
13+
}];
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { Box, Text } from 'ink';
3+
import { theme } from '../style/theme.js';
4+
export default function HelpPage() {
5+
const helpItems = [{
6+
title: '帮助主页',
7+
path: 'doc/wiki/Home.md'
8+
}, {
9+
title: '加速服务使用说明',
10+
path: 'doc/wiki/加速服务使用说明.md'
11+
}, {
12+
title: '各平台安装说明',
13+
path: 'doc/wiki/各平台安装说明.md'
14+
}, {
15+
title: '解决Github访问不了或速度很慢的问题',
16+
path: 'doc/wiki/解决Github访问不了或速度很慢的问题.md'
17+
}];
18+
return /*#__PURE__*/React.createElement(Box, {
19+
flexDirection: "column"
20+
}, /*#__PURE__*/React.createElement(Text, {
21+
bold: true,
22+
color: theme.colors.primary
23+
}, "\u5E2E\u52A9\u4E2D\u5FC3"), /*#__PURE__*/React.createElement(Box, {
24+
marginTop: 1,
25+
flexDirection: "column"
26+
}, /*#__PURE__*/React.createElement(Text, null, "\u66F4\u591A\u5185\u5BB9\u53C2\u8003:"), helpItems.map((item, index) => /*#__PURE__*/React.createElement(Box, {
27+
key: index,
28+
marginLeft: 2
29+
}, /*#__PURE__*/React.createElement(Text, {
30+
color: theme.colors.text
31+
}, "- ", item.title, ": "), /*#__PURE__*/React.createElement(Text, {
32+
color: theme.colors.muted
33+
}, item.path)))), /*#__PURE__*/React.createElement(Box, {
34+
marginTop: 1
35+
}, /*#__PURE__*/React.createElement(Text, null, "\u95EE\u9898\u53CD\u9988: "), /*#__PURE__*/React.createElement(Text, {
36+
color: theme.colors.accent
37+
}, "https://github.com/docmirror/dev-sidecar/issues")));
38+
}

0 commit comments

Comments
 (0)