@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.
Files changed (145) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/LICENSE +21 -0
  3. package/README.md +462 -0
  4. package/bin/mihomo-tui +34 -0
  5. package/dist/App.js +161 -0
  6. package/dist/App.js.map +1 -0
  7. package/dist/api/client.js +239 -0
  8. package/dist/api/client.js.map +1 -0
  9. package/dist/api/status.js +43 -0
  10. package/dist/api/status.js.map +1 -0
  11. package/dist/api/stream.js +139 -0
  12. package/dist/api/stream.js.map +1 -0
  13. package/dist/api/types.js +6 -0
  14. package/dist/api/types.js.map +1 -0
  15. package/dist/cli.js +221 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/commands/conn.js +122 -0
  18. package/dist/commands/conn.js.map +1 -0
  19. package/dist/commands/logs.js +99 -0
  20. package/dist/commands/logs.js.map +1 -0
  21. package/dist/commands/output.js +177 -0
  22. package/dist/commands/output.js.map +1 -0
  23. package/dist/commands/provider.js +140 -0
  24. package/dist/commands/provider.js.map +1 -0
  25. package/dist/commands/proxy.js +161 -0
  26. package/dist/commands/proxy.js.map +1 -0
  27. package/dist/commands/proxyEnv.js +193 -0
  28. package/dist/commands/proxyEnv.js.map +1 -0
  29. package/dist/commands/rules.js +87 -0
  30. package/dist/commands/rules.js.map +1 -0
  31. package/dist/commands/status.js +75 -0
  32. package/dist/commands/status.js.map +1 -0
  33. package/dist/components/ConfirmDialog.js +31 -0
  34. package/dist/components/ConfirmDialog.js.map +1 -0
  35. package/dist/components/DelayBadge.js +27 -0
  36. package/dist/components/DelayBadge.js.map +1 -0
  37. package/dist/components/InputDialog.js +325 -0
  38. package/dist/components/InputDialog.js.map +1 -0
  39. package/dist/components/ListDialog.js +86 -0
  40. package/dist/components/ListDialog.js.map +1 -0
  41. package/dist/components/ProgressDialog.js +34 -0
  42. package/dist/components/ProgressDialog.js.map +1 -0
  43. package/dist/components/ScrollList.js +26 -0
  44. package/dist/components/ScrollList.js.map +1 -0
  45. package/dist/components/StatusBar.js +58 -0
  46. package/dist/components/StatusBar.js.map +1 -0
  47. package/dist/components/TextEditor.js +112 -0
  48. package/dist/components/TextEditor.js.map +1 -0
  49. package/dist/components/textEditorModel.js +154 -0
  50. package/dist/components/textEditorModel.js.map +1 -0
  51. package/dist/config/manager.js +296 -0
  52. package/dist/config/manager.js.map +1 -0
  53. package/dist/config/preserveGroups.js +9 -0
  54. package/dist/config/preserveGroups.js.map +1 -0
  55. package/dist/config/ruleDocument.js +88 -0
  56. package/dist/config/ruleDocument.js.map +1 -0
  57. package/dist/config/ruleService.js +256 -0
  58. package/dist/config/ruleService.js.map +1 -0
  59. package/dist/config/ruleValidation.js +98 -0
  60. package/dist/config/ruleValidation.js.map +1 -0
  61. package/dist/config/service.js +63 -0
  62. package/dist/config/service.js.map +1 -0
  63. package/dist/config/settingsService.js +162 -0
  64. package/dist/config/settingsService.js.map +1 -0
  65. package/dist/config/skeleton.js +324 -0
  66. package/dist/config/skeleton.js.map +1 -0
  67. package/dist/config/subscriptionService.js +284 -0
  68. package/dist/config/subscriptionService.js.map +1 -0
  69. package/dist/config/subscriptions.js +200 -0
  70. package/dist/config/subscriptions.js.map +1 -0
  71. package/dist/config/types.js +5 -0
  72. package/dist/config/types.js.map +1 -0
  73. package/dist/config.js +101 -0
  74. package/dist/config.js.map +1 -0
  75. package/dist/hooks/useProviders.js +138 -0
  76. package/dist/hooks/useProviders.js.map +1 -0
  77. package/dist/hooks/useProxies.js +298 -0
  78. package/dist/hooks/useProxies.js.map +1 -0
  79. package/dist/hooks/useStream.js +127 -0
  80. package/dist/hooks/useStream.js.map +1 -0
  81. package/dist/kernel/installer.js +317 -0
  82. package/dist/kernel/installer.js.map +1 -0
  83. package/dist/kernel/releases.js +111 -0
  84. package/dist/kernel/releases.js.map +1 -0
  85. package/dist/kernel/sources.js +98 -0
  86. package/dist/kernel/sources.js.map +1 -0
  87. package/dist/rules/editor.js +181 -0
  88. package/dist/rules/editor.js.map +1 -0
  89. package/dist/rules/matcher.js +83 -0
  90. package/dist/rules/matcher.js.map +1 -0
  91. package/dist/rules/model.js +25 -0
  92. package/dist/rules/model.js.map +1 -0
  93. package/dist/rules/runtime.js +46 -0
  94. package/dist/rules/runtime.js.map +1 -0
  95. package/dist/rules/types.js +10 -0
  96. package/dist/rules/types.js.map +1 -0
  97. package/dist/ui/FooterLine.js +62 -0
  98. package/dist/ui/FooterLine.js.map +1 -0
  99. package/dist/ui/Panel.js +47 -0
  100. package/dist/ui/Panel.js.map +1 -0
  101. package/dist/ui/RowIndicator.js +25 -0
  102. package/dist/ui/RowIndicator.js.map +1 -0
  103. package/dist/ui/SectionHeader.js +14 -0
  104. package/dist/ui/SectionHeader.js.map +1 -0
  105. package/dist/ui/StatusDot.js +15 -0
  106. package/dist/ui/StatusDot.js.map +1 -0
  107. package/dist/ui/TopBar.js +81 -0
  108. package/dist/ui/TopBar.js.map +1 -0
  109. package/dist/ui/exitGuard.js +20 -0
  110. package/dist/ui/exitGuard.js.map +1 -0
  111. package/dist/ui/keyCapture.js +28 -0
  112. package/dist/ui/keyCapture.js.map +1 -0
  113. package/dist/ui/layout.js +32 -0
  114. package/dist/ui/layout.js.map +1 -0
  115. package/dist/ui/rowIndicator.js +42 -0
  116. package/dist/ui/rowIndicator.js.map +1 -0
  117. package/dist/ui/tableHeader.js +33 -0
  118. package/dist/ui/tableHeader.js.map +1 -0
  119. package/dist/ui/theme.js +47 -0
  120. package/dist/ui/theme.js.map +1 -0
  121. package/dist/ui/tone.js +131 -0
  122. package/dist/ui/tone.js.map +1 -0
  123. package/dist/version.js +27 -0
  124. package/dist/version.js.map +1 -0
  125. package/dist/views/Conns.js +107 -0
  126. package/dist/views/Conns.js.map +1 -0
  127. package/dist/views/Logs.js +89 -0
  128. package/dist/views/Logs.js.map +1 -0
  129. package/dist/views/Providers.js +392 -0
  130. package/dist/views/Providers.js.map +1 -0
  131. package/dist/views/Proxies.js +233 -0
  132. package/dist/views/Proxies.js.map +1 -0
  133. package/dist/views/RuleEditor.js +356 -0
  134. package/dist/views/RuleEditor.js.map +1 -0
  135. package/dist/views/Rules.js +286 -0
  136. package/dist/views/Rules.js.map +1 -0
  137. package/dist/views/Settings.js +734 -0
  138. package/dist/views/Settings.js.map +1 -0
  139. package/dist/views/groupDisplay.js +53 -0
  140. package/dist/views/groupDisplay.js.map +1 -0
  141. package/dist/views/subscriptionFields.js +59 -0
  142. package/dist/views/subscriptionFields.js.map +1 -0
  143. package/dist/views/yamlFile.js +32 -0
  144. package/dist/views/yamlFile.js.map +1 -0
  145. 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"}
@@ -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"}
@@ -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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"}
@@ -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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"}