@morndream/mihomo-tui 0.5.0-rc.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 +93 -0
- package/LICENSE +21 -0
- package/README.md +462 -0
- package/bin/mihomo-tui +34 -0
- package/dist/App.js +161 -0
- package/dist/App.js.map +1 -0
- package/dist/api/client.js +239 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/status.js +43 -0
- package/dist/api/status.js.map +1 -0
- package/dist/api/stream.js +139 -0
- package/dist/api/stream.js.map +1 -0
- package/dist/api/types.js +6 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli.js +221 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/conn.js +122 -0
- package/dist/commands/conn.js.map +1 -0
- package/dist/commands/logs.js +99 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/output.js +177 -0
- package/dist/commands/output.js.map +1 -0
- package/dist/commands/provider.js +140 -0
- package/dist/commands/provider.js.map +1 -0
- package/dist/commands/proxy.js +161 -0
- package/dist/commands/proxy.js.map +1 -0
- package/dist/commands/proxyEnv.js +193 -0
- package/dist/commands/proxyEnv.js.map +1 -0
- package/dist/commands/rules.js +87 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/status.js +75 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/components/ConfirmDialog.js +31 -0
- package/dist/components/ConfirmDialog.js.map +1 -0
- package/dist/components/DelayBadge.js +27 -0
- package/dist/components/DelayBadge.js.map +1 -0
- package/dist/components/InputDialog.js +325 -0
- package/dist/components/InputDialog.js.map +1 -0
- package/dist/components/ListDialog.js +86 -0
- package/dist/components/ListDialog.js.map +1 -0
- package/dist/components/ProgressDialog.js +34 -0
- package/dist/components/ProgressDialog.js.map +1 -0
- package/dist/components/ScrollList.js +26 -0
- package/dist/components/ScrollList.js.map +1 -0
- package/dist/components/StatusBar.js +58 -0
- package/dist/components/StatusBar.js.map +1 -0
- package/dist/components/TextEditor.js +112 -0
- package/dist/components/TextEditor.js.map +1 -0
- package/dist/components/textEditorModel.js +154 -0
- package/dist/components/textEditorModel.js.map +1 -0
- package/dist/config/manager.js +296 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/preserveGroups.js +9 -0
- package/dist/config/preserveGroups.js.map +1 -0
- package/dist/config/ruleDocument.js +88 -0
- package/dist/config/ruleDocument.js.map +1 -0
- package/dist/config/ruleService.js +256 -0
- package/dist/config/ruleService.js.map +1 -0
- package/dist/config/ruleValidation.js +98 -0
- package/dist/config/ruleValidation.js.map +1 -0
- package/dist/config/service.js +63 -0
- package/dist/config/service.js.map +1 -0
- package/dist/config/settingsService.js +162 -0
- package/dist/config/settingsService.js.map +1 -0
- package/dist/config/skeleton.js +324 -0
- package/dist/config/skeleton.js.map +1 -0
- package/dist/config/subscriptionService.js +284 -0
- package/dist/config/subscriptionService.js.map +1 -0
- package/dist/config/subscriptions.js +200 -0
- package/dist/config/subscriptions.js.map +1 -0
- package/dist/config/types.js +5 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/useProviders.js +138 -0
- package/dist/hooks/useProviders.js.map +1 -0
- package/dist/hooks/useProxies.js +298 -0
- package/dist/hooks/useProxies.js.map +1 -0
- package/dist/hooks/useStream.js +127 -0
- package/dist/hooks/useStream.js.map +1 -0
- package/dist/kernel/installer.js +317 -0
- package/dist/kernel/installer.js.map +1 -0
- package/dist/kernel/releases.js +111 -0
- package/dist/kernel/releases.js.map +1 -0
- package/dist/kernel/sources.js +98 -0
- package/dist/kernel/sources.js.map +1 -0
- package/dist/rules/editor.js +181 -0
- package/dist/rules/editor.js.map +1 -0
- package/dist/rules/matcher.js +83 -0
- package/dist/rules/matcher.js.map +1 -0
- package/dist/rules/model.js +25 -0
- package/dist/rules/model.js.map +1 -0
- package/dist/rules/runtime.js +46 -0
- package/dist/rules/runtime.js.map +1 -0
- package/dist/rules/types.js +10 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/ui/FooterLine.js +62 -0
- package/dist/ui/FooterLine.js.map +1 -0
- package/dist/ui/Panel.js +47 -0
- package/dist/ui/Panel.js.map +1 -0
- package/dist/ui/RowIndicator.js +25 -0
- package/dist/ui/RowIndicator.js.map +1 -0
- package/dist/ui/SectionHeader.js +14 -0
- package/dist/ui/SectionHeader.js.map +1 -0
- package/dist/ui/StatusDot.js +15 -0
- package/dist/ui/StatusDot.js.map +1 -0
- package/dist/ui/TopBar.js +81 -0
- package/dist/ui/TopBar.js.map +1 -0
- package/dist/ui/exitGuard.js +20 -0
- package/dist/ui/exitGuard.js.map +1 -0
- package/dist/ui/keyCapture.js +28 -0
- package/dist/ui/keyCapture.js.map +1 -0
- package/dist/ui/layout.js +32 -0
- package/dist/ui/layout.js.map +1 -0
- package/dist/ui/rowIndicator.js +42 -0
- package/dist/ui/rowIndicator.js.map +1 -0
- package/dist/ui/tableHeader.js +33 -0
- package/dist/ui/tableHeader.js.map +1 -0
- package/dist/ui/theme.js +47 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/ui/tone.js +131 -0
- package/dist/ui/tone.js.map +1 -0
- package/dist/version.js +27 -0
- package/dist/version.js.map +1 -0
- package/dist/views/Conns.js +107 -0
- package/dist/views/Conns.js.map +1 -0
- package/dist/views/Logs.js +89 -0
- package/dist/views/Logs.js.map +1 -0
- package/dist/views/Providers.js +392 -0
- package/dist/views/Providers.js.map +1 -0
- package/dist/views/Proxies.js +233 -0
- package/dist/views/Proxies.js.map +1 -0
- package/dist/views/RuleEditor.js +356 -0
- package/dist/views/RuleEditor.js.map +1 -0
- package/dist/views/Rules.js +286 -0
- package/dist/views/Rules.js.map +1 -0
- package/dist/views/Settings.js +734 -0
- package/dist/views/Settings.js.map +1 -0
- package/dist/views/groupDisplay.js +53 -0
- package/dist/views/groupDisplay.js.map +1 -0
- package/dist/views/subscriptionFields.js +59 -0
- package/dist/views/subscriptionFields.js.map +1 -0
- package/dist/views/yamlFile.js +32 -0
- package/dist/views/yamlFile.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* 顶栏卡片条(App 外壳):品牌标题嵌边框,左侧标签页胶囊、右侧模式与内核状态。
|
|
4
|
+
* 纯展示组件 —— 键盘处理仍在 App(ui 层不碰输入与 API)。
|
|
5
|
+
*
|
|
6
|
+
* 左右分组间距用纯函数计算(topBarGap),不依赖 flex 布局的 space-between,
|
|
7
|
+
* 保证任何终端下右侧状态都精确贴齐面板右缘。
|
|
8
|
+
*
|
|
9
|
+
* 响应式布局(2026-09-22 标准化美化):
|
|
10
|
+
* - Full(≥100列):tab 均布居中,无数字,选中 accent 加粗
|
|
11
|
+
* - Compact(72~99列):tab 紧凑排布,无数字
|
|
12
|
+
* - TooSmall(<72列):显示降级提示
|
|
13
|
+
*/
|
|
14
|
+
import { Box, Text } from 'ink';
|
|
15
|
+
import { Panel } from './Panel.js';
|
|
16
|
+
import { StatusDot } from './StatusDot.js';
|
|
17
|
+
import { colors, styles } from './theme.js';
|
|
18
|
+
import { displayWidth } from '../commands/output.js';
|
|
19
|
+
import { classifySize } from './layout.js';
|
|
20
|
+
/** 标签行的纯文本(宽度计算用):选中加粗,无数字,2 空格分隔 */
|
|
21
|
+
export function tabRowText(tabs, active) {
|
|
22
|
+
return tabs.map((name, index) => (index === active ? name : name)).join(' ');
|
|
23
|
+
}
|
|
24
|
+
/** 右侧状态的纯文本(宽度计算用;● 按一列计) */
|
|
25
|
+
export function statusText(modeLabel, apiPort) {
|
|
26
|
+
return `${modeLabel} · ${apiPort} ●`;
|
|
27
|
+
}
|
|
28
|
+
/** 单格宽度上限:宽终端封顶,标签块整体居中(spec 2026-09-13 dialogs-topbar-polish §3.3) */
|
|
29
|
+
export const TAB_CELL_CAP = 12;
|
|
30
|
+
/** 标签均布(纯函数):availWidth 均分成 count 格(封顶 TAB_CELL_CAP),标签在格内居中;
|
|
31
|
+
* 格宽装不下最宽标签(含 2 列间隙)时返回 null,调用方回退紧凑布局。
|
|
32
|
+
* 标签数量增减 → 格宽自动重算,未来顶栏扩展无需改渲染。
|
|
33
|
+
* 2026-09-22:去掉数字,选中不加前缀箭头。 */
|
|
34
|
+
export function tabCells(tabs, active, availWidth) {
|
|
35
|
+
const count = tabs.length;
|
|
36
|
+
if (count === 0)
|
|
37
|
+
return null; // 空标签表走紧凑回退,避免调用方除零
|
|
38
|
+
const cellWidth = Math.min(Math.floor(availWidth / count), TAB_CELL_CAP);
|
|
39
|
+
const labelWidths = tabs.map((name) => displayWidth(name));
|
|
40
|
+
const minNeeded = Math.max(...labelWidths) + 2;
|
|
41
|
+
if (cellWidth < minNeeded)
|
|
42
|
+
return null;
|
|
43
|
+
return tabs.map((name, index) => {
|
|
44
|
+
const labelWidth = labelWidths[index] ?? 0;
|
|
45
|
+
const pad = Math.max(0, cellWidth - labelWidth);
|
|
46
|
+
const left = Math.floor(pad / 2);
|
|
47
|
+
return {
|
|
48
|
+
text: ' '.repeat(left) + name + ' '.repeat(pad - left),
|
|
49
|
+
active: index === active,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** 左右分组之间的填充空格数(纯函数,至少 1) */
|
|
54
|
+
export function topBarGap(tabs, active, modeLabel, apiPort, innerWidth) {
|
|
55
|
+
const left = displayWidth(tabRowText(tabs, active));
|
|
56
|
+
const right = displayWidth(statusText(modeLabel, apiPort));
|
|
57
|
+
return Math.max(1, innerWidth - left - right);
|
|
58
|
+
}
|
|
59
|
+
export function TopBar({ tabs, active, modeLabel, apiPort, statusTone, width }) {
|
|
60
|
+
const sizeClass = classifySize(width);
|
|
61
|
+
// TooSmall:显示降级提示
|
|
62
|
+
if (sizeClass === 'TooSmall') {
|
|
63
|
+
return (_jsx(Panel, { title: "\u25C6 mihomo-tui", width: width, children: _jsxs(Box, { flexDirection: "column", alignItems: "center", paddingY: 1, children: [_jsx(Text, { color: colors.muted, children: "mihomo-tui \u9700\u8981\u81F3\u5C11 72 \u5217\u5BBD\u7EC8\u7AEF" }), _jsx(Text, { color: colors.muted, children: "\u8BF7\u8C03\u6574\u7A97\u53E3\u5927\u5C0F\u540E\u7EE7\u7EED" })] }) }));
|
|
64
|
+
}
|
|
65
|
+
const innerWidth = Math.max(0, width - 4);
|
|
66
|
+
const statusWidth = displayWidth(`${modeLabel} · ${apiPort} `) + 1; // StatusDot 占 1 列
|
|
67
|
+
const availWidth = Math.max(0, innerWidth - statusWidth);
|
|
68
|
+
const cells = sizeClass === 'Full' ? tabCells(tabs, active, availWidth) : null;
|
|
69
|
+
const gap = topBarGap(tabs, active, modeLabel, apiPort, innerWidth);
|
|
70
|
+
// Full 布局:标签均布居中
|
|
71
|
+
if (cells) {
|
|
72
|
+
const cellWidth = displayWidth(cells[0].text);
|
|
73
|
+
const extra = Math.max(0, availWidth - cellWidth * cells.length);
|
|
74
|
+
const leftPad = Math.floor(extra / 2);
|
|
75
|
+
const rightGap = extra - leftPad;
|
|
76
|
+
return (_jsx(Panel, { title: "\u25C6 mihomo-tui", width: width, children: _jsxs(Text, { children: [_jsx(Text, { children: ' '.repeat(leftPad) }), cells.map((cell, index) => (_jsx(Text, { children: cell.active ? (_jsx(Text, { ...styles.rowFocus, bold: true, children: cell.text })) : (_jsx(Text, { color: colors.muted, children: cell.text })) }, tabs[index] ?? index))), _jsx(Text, { children: ' '.repeat(rightGap) }), _jsx(Text, { color: colors.muted, children: `${modeLabel} · ${apiPort} ` }), _jsx(StatusDot, { tone: statusTone })] }) }));
|
|
77
|
+
}
|
|
78
|
+
// Compact 布局:紧凑排布,2 空格分隔
|
|
79
|
+
return (_jsx(Panel, { title: "\u25C6 mihomo-tui", width: width, children: _jsxs(Text, { children: [tabs.map((name, index) => (_jsxs(Text, { children: [index > 0 ? _jsx(Text, { children: ' ' }) : null, index === active ? (_jsx(Text, { ...styles.rowFocus, bold: true, children: name })) : (_jsx(Text, { color: colors.muted, children: name }))] }, name))), _jsx(Text, { children: ' '.repeat(gap) }), _jsx(Text, { color: colors.muted, children: `${modeLabel} · ${apiPort} ` }), _jsx(StatusDot, { tone: statusTone })] }) }));
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=TopBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopBar.js","sourceRoot":"","sources":["../../src/ui/TopBar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
const guards = new Map();
|
|
3
|
+
/** Separate from ordinary key capture: Ctrl+C must consult transaction guards first. */
|
|
4
|
+
export function requestGuardedExit(exit) {
|
|
5
|
+
const guard = [...guards.values()].at(-1);
|
|
6
|
+
if (!guard)
|
|
7
|
+
return false;
|
|
8
|
+
guard(exit);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
export function useExitGuard(handler) {
|
|
12
|
+
const latest = useRef(handler);
|
|
13
|
+
latest.current = handler;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const token = Symbol('rule-exit');
|
|
16
|
+
guards.set(token, exit => latest.current(exit));
|
|
17
|
+
return () => { guards.delete(token); };
|
|
18
|
+
}, []);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=exitGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exitGuard.js","sourceRoot":"","sources":["../../src/ui/exitGuard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAEzC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsC,CAAA;AAC5D,wFAAwF;AACxF,MAAM,UAAU,kBAAkB,CAAC,IAAgB;IACjD,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,KAAK,CAAC,IAAI,CAAC,CAAA;IACX,OAAO,IAAI,CAAA;AACb,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,OAAmC;IAC9D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;IAC9B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAA;QACjC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QAC/C,OAAO,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA,CAAC,CAAC,CAAA;IACvC,CAAC,EAAE,EAAE,CAAC,CAAA;AACR,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 按键捕获登记表:ink 的 useInput 是广播式的(所有挂载的 hook 都收到同一
|
|
3
|
+
* 按键),没有「内层优先消费」的机制。谁在捕获按键谁在这里登记,App 的
|
|
4
|
+
* 全局 handler 见登记即让路——移植 cc-switch 的内层守卫范式(它的实现靠
|
|
5
|
+
* 事件冒泡 + defaultPrevented,ink 没有等价物)。
|
|
6
|
+
*/
|
|
7
|
+
import { useEffect } from 'react';
|
|
8
|
+
const captured = new Set();
|
|
9
|
+
/** 当前是否有组件正在捕获全部按键(对话框、文本编辑态、二次确认态等) */
|
|
10
|
+
export function keysCaptured() {
|
|
11
|
+
return captured.size > 0;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 声明本组件在 active 期间捕获全部按键。挂载即登记、卸载即注销,
|
|
15
|
+
* 状态切换(如日志过滤编辑态)传布尔值即可。
|
|
16
|
+
*/
|
|
17
|
+
export function useKeyCapture(active) {
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!active)
|
|
20
|
+
return;
|
|
21
|
+
const token = Symbol('key-capture');
|
|
22
|
+
captured.add(token);
|
|
23
|
+
return () => {
|
|
24
|
+
captured.delete(token);
|
|
25
|
+
};
|
|
26
|
+
}, [active]);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=keyCapture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyCapture.js","sourceRoot":"","sources":["../../src/ui/keyCapture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;AAElC,wCAAwC;AACxC,MAAM,UAAU,YAAY;IAC1B,OAAO,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAA;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAe;IAC3C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAM;QACnB,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,CAAA;QACnC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACnB,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxB,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 响应式布局系统(2026-09-22 标准化美化)
|
|
3
|
+
*
|
|
4
|
+
* 对齐 mihari 的三档分级:TooSmall/Compact/Full,
|
|
5
|
+
* 为顶栏、页脚、各页面提供统一的宽度分类与降级策略。
|
|
6
|
+
*/
|
|
7
|
+
/** 宽度阈值常量 */
|
|
8
|
+
export const SIZE_THRESHOLDS = {
|
|
9
|
+
/** 最小可用宽度:低于此值显示降级提示 */
|
|
10
|
+
MIN_USABLE: 72,
|
|
11
|
+
/** 进入 Full 布局的宽度:≥100 列启用均布居中等高级布局 */
|
|
12
|
+
FULL_LAYOUT: 100,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* 根据终端列宽分类布局档位(纯函数)
|
|
16
|
+
*
|
|
17
|
+
* @param columns 终端列数
|
|
18
|
+
* @returns SizeClass 档位
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* classifySize(120) // => 'Full'
|
|
22
|
+
* classifySize(80) // => 'Compact'
|
|
23
|
+
* classifySize(60) // => 'TooSmall'
|
|
24
|
+
*/
|
|
25
|
+
export function classifySize(columns) {
|
|
26
|
+
if (columns < SIZE_THRESHOLDS.MIN_USABLE)
|
|
27
|
+
return 'TooSmall';
|
|
28
|
+
if (columns < SIZE_THRESHOLDS.FULL_LAYOUT)
|
|
29
|
+
return 'Compact';
|
|
30
|
+
return 'Full';
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/ui/layout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,aAAa;AACb,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,wBAAwB;IACxB,UAAU,EAAE,EAAE;IACd,sCAAsC;IACtC,WAAW,EAAE,GAAG;CACR,CAAA;AAEV;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,IAAI,OAAO,GAAG,eAAe,CAAC,UAAU;QAAE,OAAO,UAAU,CAAA;IAC3D,IAAI,OAAO,GAAG,eAAe,CAAC,WAAW;QAAE,OAAO,SAAS,CAAA;IAC3D,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 行焦点符号统一 —— 全 app 行首指示符(提升自 Proxies.tsx)
|
|
3
|
+
*
|
|
4
|
+
* 符号语义:
|
|
5
|
+
* ▌ = 键盘焦点行(accent 色)
|
|
6
|
+
* ❯ = 驻留光标(muted,仅双栏设置页另一栏无焦点时)
|
|
7
|
+
* ● = 业务选中标记(accent 加粗,如节点页已选节点)
|
|
8
|
+
* 空格 = 无焦点/未选中
|
|
9
|
+
*/
|
|
10
|
+
import { colors } from './theme.js';
|
|
11
|
+
/**
|
|
12
|
+
* 行焦点符号(纯函数,可测)
|
|
13
|
+
*
|
|
14
|
+
* @param focused - 是否键盘焦点行
|
|
15
|
+
* @param selected - 是否业务选中(如节点页已选节点)
|
|
16
|
+
* @returns 符号字符串('▌'/'●'/空格)
|
|
17
|
+
*/
|
|
18
|
+
export function rowBar(focused, selected) {
|
|
19
|
+
if (focused)
|
|
20
|
+
return '▌';
|
|
21
|
+
if (selected)
|
|
22
|
+
return '●';
|
|
23
|
+
return ' ';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 行焦点符号颜色(纯函数,可测)
|
|
27
|
+
*
|
|
28
|
+
* @param focused - 是否键盘焦点行
|
|
29
|
+
* @returns ANSI 颜色字符串
|
|
30
|
+
*/
|
|
31
|
+
export function rowBarColor(focused) {
|
|
32
|
+
return focused ? colors.accent : colors.accent;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 驻留光标符号(双栏页面另一栏用)
|
|
36
|
+
*
|
|
37
|
+
* @returns '❯'(muted 色,由调用方着色)
|
|
38
|
+
*/
|
|
39
|
+
export function cursorBar() {
|
|
40
|
+
return '❯';
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=rowIndicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rowIndicator.js","sourceRoot":"","sources":["../../src/ui/rowIndicator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAEnC;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,OAAgB,EAAE,QAAiB;IACxD,IAAI,OAAO;QAAE,OAAO,GAAG,CAAA;IACvB,IAAI,QAAQ;QAAE,OAAO,GAAG,CAAA;IACxB,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAA;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 表头渲染函数 —— 连接页表头标准(对齐 mihari table header)
|
|
3
|
+
*
|
|
4
|
+
* 用法:
|
|
5
|
+
* renderTableHeader(['HOST', 'CHAIN', '↑', '↓', 'TIME'], [40, 20, 12, 12, 10])
|
|
6
|
+
*/
|
|
7
|
+
import { styles } from './theme.js';
|
|
8
|
+
/**
|
|
9
|
+
* 渲染表头行(纯函数,可测)
|
|
10
|
+
*
|
|
11
|
+
* @param headers - 表头文本数组
|
|
12
|
+
* @param widths - 各列宽度数组
|
|
13
|
+
* @returns 格式化的表头字符串(dimColor + bold)
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* renderTableHeader(['HOST', 'CHAIN'], [40, 20])
|
|
17
|
+
* // => ' HOST CHAIN '
|
|
18
|
+
*/
|
|
19
|
+
export function renderTableHeader(headers, widths) {
|
|
20
|
+
if (headers.length !== widths.length) {
|
|
21
|
+
throw new Error('renderTableHeader: headers 与 widths 长度不匹配');
|
|
22
|
+
}
|
|
23
|
+
const cells = headers.map((header, i) => {
|
|
24
|
+
const width = widths[i];
|
|
25
|
+
return header.padEnd(width).slice(0, width);
|
|
26
|
+
});
|
|
27
|
+
return ' ' + cells.join('');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 表头样式对象(供 <Text> 展开)
|
|
31
|
+
*/
|
|
32
|
+
export const tableHeaderStyle = styles.tableHeader;
|
|
33
|
+
//# sourceMappingURL=tableHeader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tableHeader.js","sourceRoot":"","sources":["../../src/ui/tableHeader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAEnC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAiB,EAAE,MAAgB;IACnE,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;IAC9D,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAA;QACxB,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,OAAO,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,WAAW,CAAA"}
|
package/dist/ui/theme.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「终端绿」主题 —— 全项目唯一取色处。
|
|
3
|
+
*
|
|
4
|
+
* 颜色用 ink 的 ansi256(N) 语法精确表达 256 色板索引,由 ink 按终端
|
|
5
|
+
* 能力决定降级;业务代码禁止硬编码色值,一律 import { colors, styles }。
|
|
6
|
+
*
|
|
7
|
+
* 语义(颜色承载含义而非装饰):
|
|
8
|
+
* accent 焦点/交互 · success 健康 · warning 注意 · danger 错误 · info 信息
|
|
9
|
+
* muted 次要/未知(关闭或未知是合法中性态,不用红) · surfaceBorder 边框
|
|
10
|
+
* onSolid 实底徽标前景(黑色,保证 256 色终端对比度)
|
|
11
|
+
*
|
|
12
|
+
* 角色样式供 <Text {...styles.xxx}> 展开;换主题只改本文件。
|
|
13
|
+
*/
|
|
14
|
+
export const colors = {
|
|
15
|
+
accent: 'ansi256(40)', // #00d700 亮终端绿
|
|
16
|
+
success: 'ansi256(71)', // #5faf5f 中绿
|
|
17
|
+
warning: 'ansi256(184)', // #d7d75f 橄榄黄
|
|
18
|
+
danger: 'ansi256(160)', // #d70000 正红
|
|
19
|
+
info: 'ansi256(73)', // #5fafaf 青碧
|
|
20
|
+
muted: 'ansi256(244)', // #808080 中灰
|
|
21
|
+
surfaceBorder: 'ansi256(238)', // #444444 暗灰
|
|
22
|
+
onSolid: 'ansi256(0)', // #000000 黑色(实底徽标前景)
|
|
23
|
+
};
|
|
24
|
+
/** 角色样式(Text 属性子集),与 colors 同源 */
|
|
25
|
+
export const styles = {
|
|
26
|
+
panelTitle: { color: colors.accent, bold: true },
|
|
27
|
+
keyCap: { color: colors.accent, bold: true },
|
|
28
|
+
keyHint: { dimColor: true },
|
|
29
|
+
// ▌/● 等指示字符属 ambiguous 宽度(极端终端 2 列),依赖行内余量吸收
|
|
30
|
+
rowFocus: { color: colors.accent },
|
|
31
|
+
rowSelected: { color: colors.accent, bold: true },
|
|
32
|
+
tableHeader: { dimColor: true, bold: true },
|
|
33
|
+
};
|
|
34
|
+
export const toneColor = {
|
|
35
|
+
neutral: colors.muted,
|
|
36
|
+
positive: colors.success,
|
|
37
|
+
caution: colors.warning,
|
|
38
|
+
negative: colors.danger,
|
|
39
|
+
};
|
|
40
|
+
/** 实底徽标背景色映射(供 StatusChip solid 形态使用) */
|
|
41
|
+
export const toneBg = {
|
|
42
|
+
neutral: 'ansi256(244)', // 灰底
|
|
43
|
+
positive: 'ansi256(34)', // 绿底
|
|
44
|
+
caution: 'ansi256(226)', // 黄底
|
|
45
|
+
negative: 'ansi256(196)', // 红底
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/ui/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAE,aAAa,EAAE,eAAe;IACtC,OAAO,EAAE,aAAa,EAAE,aAAa;IACrC,OAAO,EAAE,cAAc,EAAE,cAAc;IACvC,MAAM,EAAE,cAAc,EAAE,aAAa;IACrC,IAAI,EAAE,aAAa,EAAE,aAAa;IAClC,KAAK,EAAE,cAAc,EAAE,aAAa;IACpC,aAAa,EAAE,cAAc,EAAE,aAAa;IAC5C,OAAO,EAAE,YAAY,EAAE,qBAAqB;CACpC,CAAA;AAIV,kCAAkC;AAClC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;IAChD,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;IAC5C,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3B,6CAA6C;IAC7C,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;IAClC,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE;IACjD,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;CACnC,CAAA;AAKV,MAAM,CAAC,MAAM,SAAS,GAAyB;IAC7C,OAAO,EAAE,MAAM,CAAC,KAAK;IACrB,QAAQ,EAAE,MAAM,CAAC,OAAO;IACxB,OAAO,EAAE,MAAM,CAAC,OAAO;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM;CACxB,CAAA;AAED,yCAAyC;AACzC,MAAM,CAAC,MAAM,MAAM,GAAyB;IAC1C,OAAO,EAAE,cAAc,EAAE,KAAK;IAC9B,QAAQ,EAAE,aAAa,EAAE,KAAK;IAC9B,OAAO,EAAE,cAAc,EAAE,KAAK;IAC9B,QAAQ,EAAE,cAAc,EAAE,KAAK;CAChC,CAAA"}
|
package/dist/ui/tone.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 状态语义分类器(2026-09-22 标准化美化)
|
|
3
|
+
*
|
|
4
|
+
* 为各类业务状态(订阅/内核/连接)提供统一的语义色映射与词汇表,
|
|
5
|
+
* 避免每个组件独立维护颜色逻辑和文案。
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 订阅状态 → 语义色(纯函数)
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* subscriptionTone('normal') // => 'positive'
|
|
12
|
+
* subscriptionTone('expired') // => 'caution'
|
|
13
|
+
*/
|
|
14
|
+
export function subscriptionTone(state) {
|
|
15
|
+
switch (state) {
|
|
16
|
+
case 'normal':
|
|
17
|
+
return 'positive';
|
|
18
|
+
case 'expiring':
|
|
19
|
+
return 'caution';
|
|
20
|
+
case 'expired':
|
|
21
|
+
return 'caution';
|
|
22
|
+
case 'unknown':
|
|
23
|
+
default:
|
|
24
|
+
return 'neutral';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 内核版本状态 → 语义色(纯函数)
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* kernelVersionTone('updateAvailable') // => 'caution'
|
|
32
|
+
* kernelVersionTone('latest') // => 'positive'
|
|
33
|
+
*/
|
|
34
|
+
export function kernelVersionTone(state) {
|
|
35
|
+
switch (state) {
|
|
36
|
+
case 'latest':
|
|
37
|
+
return 'positive';
|
|
38
|
+
case 'updateAvailable':
|
|
39
|
+
return 'caution';
|
|
40
|
+
case 'checking':
|
|
41
|
+
case 'unknown':
|
|
42
|
+
default:
|
|
43
|
+
return 'neutral';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 订阅状态中文词表(纯函数)
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* subscriptionLabel('normal') // => '正常'
|
|
51
|
+
* subscriptionLabel('expired') // => '已过期'
|
|
52
|
+
*/
|
|
53
|
+
export function subscriptionLabel(state) {
|
|
54
|
+
switch (state) {
|
|
55
|
+
case 'normal':
|
|
56
|
+
return '正常';
|
|
57
|
+
case 'expiring':
|
|
58
|
+
return '即将到期';
|
|
59
|
+
case 'expired':
|
|
60
|
+
return '已过期';
|
|
61
|
+
case 'unknown':
|
|
62
|
+
default:
|
|
63
|
+
return '未知';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 内核版本状态中文词表(纯函数)
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* kernelVersionLabel('updateAvailable') // => '可更新'
|
|
71
|
+
* kernelVersionLabel('latest') // => '最新'
|
|
72
|
+
*/
|
|
73
|
+
export function kernelVersionLabel(state) {
|
|
74
|
+
switch (state) {
|
|
75
|
+
case 'latest':
|
|
76
|
+
return '最新';
|
|
77
|
+
case 'updateAvailable':
|
|
78
|
+
return '可更新';
|
|
79
|
+
case 'checking':
|
|
80
|
+
return '检查中';
|
|
81
|
+
case 'unknown':
|
|
82
|
+
default:
|
|
83
|
+
return '未知';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 通用状态文本 → 语义色分类器(纯函数)
|
|
88
|
+
*
|
|
89
|
+
* 支持中英文关键词匹配,用于业务层从任意状态字符串推断语义色调。
|
|
90
|
+
* 子串匹配、忽略大小写、忽略前后空格。
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* classifyTone('connected') // => 'positive'
|
|
94
|
+
* classifyTone('重连中') // => 'caution'
|
|
95
|
+
* classifyTone('failed') // => 'negative'
|
|
96
|
+
* classifyTone('unknown') // => 'neutral'
|
|
97
|
+
*/
|
|
98
|
+
export function classifyTone(text) {
|
|
99
|
+
const normalized = text.trim().toLowerCase();
|
|
100
|
+
// Negative 故障态:断开、失败、错误、已过期(优先级最高,避免被 connected 子串误判)
|
|
101
|
+
if (normalized.includes('disconnect') ||
|
|
102
|
+
normalized.includes('failed') ||
|
|
103
|
+
normalized.includes('error') ||
|
|
104
|
+
normalized.includes('断开') ||
|
|
105
|
+
normalized.includes('失败') ||
|
|
106
|
+
normalized.includes('已过期')) {
|
|
107
|
+
return 'negative';
|
|
108
|
+
}
|
|
109
|
+
// Positive 健康态:已连接、运行中、成功、正常
|
|
110
|
+
if (normalized.includes('connected') ||
|
|
111
|
+
normalized.includes('running') ||
|
|
112
|
+
normalized.includes('success') ||
|
|
113
|
+
normalized.includes('healthy') ||
|
|
114
|
+
normalized.includes('已连接') ||
|
|
115
|
+
normalized.includes('运行中') ||
|
|
116
|
+
normalized.includes('正常')) {
|
|
117
|
+
return 'positive';
|
|
118
|
+
}
|
|
119
|
+
// Caution 注意态:重连中、警告、可更新、即将到期
|
|
120
|
+
if (normalized.includes('reconnecting') ||
|
|
121
|
+
normalized.includes('warning') ||
|
|
122
|
+
normalized.includes('pending') ||
|
|
123
|
+
normalized.includes('重连中') ||
|
|
124
|
+
normalized.includes('可更新') ||
|
|
125
|
+
normalized.includes('即将到期')) {
|
|
126
|
+
return 'caution';
|
|
127
|
+
}
|
|
128
|
+
// Neutral 中性态:未知、空闲、已关闭、其他未匹配文本
|
|
129
|
+
return 'neutral';
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=tone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tone.js","sourceRoot":"","sources":["../../src/ui/tone.ts"],"names":[],"mappings":"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"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 版本号来源链:编译期注入 > 环境变量 > package.json > '0.0.0-dev' 哨兵。
|
|
3
|
+
* bun build --compile 的产物里没有 package.json,import.meta.url 指向虚拟路径,
|
|
4
|
+
* 读盘必炸——编译期必须 --define 注入;其余运行形态(tsx 源码、dist、npm 发布包)
|
|
5
|
+
* 都读得到 package.json,兜底保证「不注入也正确」。产物若误打出 0.0.0-dev,
|
|
6
|
+
* 即为 define 未生效的诊断信号。
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
export const VERSION = nonEmpty(globalThis.__MIHOMO_TUI_VERSION__) ??
|
|
10
|
+
nonEmpty(process.env['MIHOMO_TUI_VERSION']) ??
|
|
11
|
+
readPackageVersion() ??
|
|
12
|
+
'0.0.0-dev';
|
|
13
|
+
/** 空串不是有效版本号:env 与注入通道都可能带来空值 */
|
|
14
|
+
function nonEmpty(value) {
|
|
15
|
+
return value ? value : undefined;
|
|
16
|
+
}
|
|
17
|
+
/** package.json 读不到(编译产物)不算错误,只是退回下一级来源 */
|
|
18
|
+
function readPackageVersion() {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
21
|
+
return typeof parsed.version === 'string' && parsed.version ? parsed.version : undefined;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAOtC,MAAM,CAAC,MAAM,OAAO,GAClB,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC3C,kBAAkB,EAAE;IACpB,WAAW,CAAA;AAEb,iCAAiC;AACjC,SAAS,QAAQ,CAAC,KAAyB;IACzC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAClC,CAAC;AAED,2CAA2C;AAC3C,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAE1F,CAAA;QACD,OAAO,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/** 标签页 5:连接管理 */
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { Box, Text, useInput } from 'ink';
|
|
5
|
+
import { MihomoClient } from '../api/client.js';
|
|
6
|
+
import { ScrollList } from '../components/ScrollList.js';
|
|
7
|
+
import { FooterLine } from '../ui/FooterLine.js';
|
|
8
|
+
import { Panel } from '../ui/Panel.js';
|
|
9
|
+
import { colors, styles } from '../ui/theme.js';
|
|
10
|
+
import { useKeyCapture } from '../ui/keyCapture.js';
|
|
11
|
+
import { formatBytes, fitDisplay, padDisplay } from '../commands/output.js';
|
|
12
|
+
const HINTS = [
|
|
13
|
+
{ key: '↑↓', label: '移动' },
|
|
14
|
+
{ key: 'd', label: '关闭选中' },
|
|
15
|
+
{ key: 'D', label: '关闭全部' },
|
|
16
|
+
{ key: 's', label: '切换排序' },
|
|
17
|
+
];
|
|
18
|
+
function describeHost(conn) {
|
|
19
|
+
const { host, sniffHost, destinationIP, destinationPort } = conn.metadata;
|
|
20
|
+
const name = host || sniffHost || destinationIP || '?';
|
|
21
|
+
return destinationPort ? `${name}:${destinationPort}` : name;
|
|
22
|
+
}
|
|
23
|
+
function formatDuration(start) {
|
|
24
|
+
const began = Date.parse(start);
|
|
25
|
+
if (Number.isNaN(began))
|
|
26
|
+
return '---';
|
|
27
|
+
const seconds = Math.max(0, Math.floor((Date.now() - began) / 1000));
|
|
28
|
+
if (seconds < 60)
|
|
29
|
+
return `${seconds}s`;
|
|
30
|
+
if (seconds < 3600)
|
|
31
|
+
return `${Math.floor(seconds / 60)}m${seconds % 60}s`;
|
|
32
|
+
return `${Math.floor(seconds / 3600)}h${Math.floor((seconds % 3600) / 60)}m`;
|
|
33
|
+
}
|
|
34
|
+
export function ConnsView({ config, data, height, width, active, onMessage }) {
|
|
35
|
+
const client = useMemo(() => new MihomoClient(config), [config]);
|
|
36
|
+
const [index, setIndex] = useState(0);
|
|
37
|
+
const [sort, setSort] = useState('traffic');
|
|
38
|
+
const [confirmAll, setConfirmAll] = useState(false);
|
|
39
|
+
// 无连接时内核返回 null 而非 [](SPEC 3.5)
|
|
40
|
+
const conns = data?.connections ?? [];
|
|
41
|
+
const sorted = useMemo(() => {
|
|
42
|
+
const list = [...conns];
|
|
43
|
+
if (sort === 'time')
|
|
44
|
+
return list.sort((a, b) => Date.parse(a.start) - Date.parse(b.start));
|
|
45
|
+
if (sort === 'host')
|
|
46
|
+
return list.sort((a, b) => describeHost(a).localeCompare(describeHost(b)));
|
|
47
|
+
return list.sort((a, b) => b.download + b.upload - (a.download + a.upload));
|
|
48
|
+
}, [conns, sort]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (index >= sorted.length)
|
|
51
|
+
setIndex(Math.max(0, sorted.length - 1));
|
|
52
|
+
}, [sorted.length, index]);
|
|
53
|
+
const current = sorted[index];
|
|
54
|
+
// D 二次确认态捕获按键
|
|
55
|
+
useKeyCapture(confirmAll);
|
|
56
|
+
useInput((input, key) => {
|
|
57
|
+
if (confirmAll) {
|
|
58
|
+
// 二次确认:只有明确按 y 才执行,其他任何键都取消
|
|
59
|
+
if (input === 'y' || input === 'Y') {
|
|
60
|
+
void client.closeAllConnections().then(() => onMessage('已关闭全部连接'));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
onMessage('已取消');
|
|
64
|
+
}
|
|
65
|
+
setConfirmAll(false);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (key.upArrow || input === 'k') {
|
|
69
|
+
setIndex((i) => Math.max(0, i - 1));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (key.downArrow || input === 'j') {
|
|
73
|
+
setIndex((i) => Math.min(sorted.length - 1, i + 1));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (input === 'd' && current) {
|
|
77
|
+
void client
|
|
78
|
+
.closeConnection(current.id)
|
|
79
|
+
.then(() => onMessage(`已关闭 ${describeHost(current)}`))
|
|
80
|
+
.catch((err) => onMessage(`关闭失败:${err instanceof Error ? err.message : String(err)}`));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (input === 'D') {
|
|
84
|
+
if (conns.length === 0) {
|
|
85
|
+
onMessage('当前无连接');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
setConfirmAll(true);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (input === 's') {
|
|
92
|
+
const order = ['traffic', 'time', 'host'];
|
|
93
|
+
const next = order[(order.indexOf(sort) + 1) % order.length] ?? 'traffic';
|
|
94
|
+
setSort(next);
|
|
95
|
+
onMessage(`排序 → ${next}`);
|
|
96
|
+
}
|
|
97
|
+
}, { isActive: active });
|
|
98
|
+
// Panel 顶线/底边 + 状态行各占 1 行(对齐订阅页设计,spec 2026-09-13 §2.4)
|
|
99
|
+
const listHeight = Math.max(3, height - 6);
|
|
100
|
+
const narrow = width < 100;
|
|
101
|
+
const hostWidth = narrow ? Math.max(18, width - 34) : Math.max(24, Math.floor(width * 0.32));
|
|
102
|
+
// 将统计信息放入标题:连接 · 计数 · 累计流量 · 排序
|
|
103
|
+
const sortLabel = sort === 'traffic' ? '流量' : sort === 'time' ? '时间' : '主机';
|
|
104
|
+
const title = `连接 · ${conns.length} · 累计 ↑${formatBytes(data?.uploadTotal ?? 0)} ↓${formatBytes(data?.downloadTotal ?? 0)} · 排序:${sortLabel}`;
|
|
105
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Box, { flexDirection: "row", flexGrow: 1, children: _jsxs(Panel, { title: title, fillHeight: true, width: width, children: [_jsxs(Text, { ...styles.tableHeader, children: [' ', padDisplay('HOST', hostWidth - 1), narrow ? '' : padDisplay('CHAIN', 24), padDisplay('↑', 10), padDisplay('↓', 10), 'TIME'] }), _jsx(ScrollList, { items: sorted, selected: index, height: listHeight, emptyText: "\u5F53\u524D\u65E0\u6D3B\u8DC3\u8FDE\u63A5", renderItem: (conn, _i, isSelected) => (_jsxs(Text, { wrap: "truncate-end", children: [isSelected ? _jsx(Text, { ...styles.rowFocus, children: '▌' }) : ' ', fitDisplay(describeHost(conn), hostWidth - 2), ' ', narrow ? '' : `${fitDisplay(conn.chains.at(0) ?? '', 23)} `, _jsx(Text, { color: colors.success, children: padDisplay(formatBytes(conn.upload), 10) }), _jsx(Text, { color: colors.success, children: padDisplay(formatBytes(conn.download), 10) }), formatDuration(conn.start)] })) })] }) }), confirmAll ? (_jsx(Panel, { danger: true, title: `确认关闭全部 ${conns.length} 条连接?`, width: width, children: _jsx(Text, { color: colors.danger, children: "\u6309 y \u786E\u8BA4\uFF0C\u5176\u4ED6\u952E\u53D6\u6D88" }) })) : null, _jsx(Box, { paddingLeft: 1, children: _jsx(FooterLine, { hints: HINTS, width: width - 2 }) })] }));
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=Conns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Conns.js","sourceRoot":"","sources":["../../src/views/Conns.tsx"],"names":[],"mappings":"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"}
|