@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,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* 进度对话框(设计稿 5.3):步骤计数、百分比条、已完成步骤清单。
|
|
4
|
+
* cancelable 时响应 ESC(仅在安全点提供——由调用方决定是否传入 onCancel)。
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text, useInput } from 'ink';
|
|
7
|
+
import { colors } from '../ui/theme.js';
|
|
8
|
+
import { useKeyCapture } from '../ui/keyCapture.js';
|
|
9
|
+
/** 纯函数:生成固定宽度的进度条,如 [#####-----]。
|
|
10
|
+
* 用 ASCII 而非 █░:块元素属 East Asian Ambiguous 宽度,在 ambiguous=wide
|
|
11
|
+
* 配置的真实终端下按 2 列渲染,一整条 10~20 字符的进度条会把行宽撑出
|
|
12
|
+
* 面板导致折行(本机 pyte 工具链测不出,属真机终端风险,保守处理)。
|
|
13
|
+
*/
|
|
14
|
+
export function progressBar(fraction, width = 20) {
|
|
15
|
+
const clamped = Math.min(1, Math.max(0, fraction));
|
|
16
|
+
const filled = Math.round(clamped * width);
|
|
17
|
+
return `[${'#'.repeat(filled)}${'-'.repeat(width - filled)}]`;
|
|
18
|
+
}
|
|
19
|
+
/** 纯函数:当前进度百分比(0–100),total 为 0 时按完成计 */
|
|
20
|
+
export function progressPercent(step, total) {
|
|
21
|
+
if (total <= 0)
|
|
22
|
+
return 100;
|
|
23
|
+
return Math.min(100, Math.round((step / total) * 100));
|
|
24
|
+
}
|
|
25
|
+
export function ProgressDialog({ title, current, total, step, completed = [], detail, cancelable = false, onCancel, width = 56, }) {
|
|
26
|
+
useKeyCapture(true);
|
|
27
|
+
useInput((_input, key) => {
|
|
28
|
+
if (cancelable && key.escape)
|
|
29
|
+
onCancel?.();
|
|
30
|
+
});
|
|
31
|
+
const percent = progressPercent(step, total);
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: colors.accent, paddingX: 1, width: width, children: [_jsx(Text, { bold: true, children: ` ${title}` }), _jsx(Text, { children: ` [${Math.min(step + 1, total)}/${total}] ${current}` }), detail ? _jsx(Text, { dimColor: true, children: ` ${detail}` }) : null, _jsx(Text, { color: colors.accent, children: ` ${progressBar(percent / 100)} ${percent}%` }), completed.map((label) => (_jsx(Text, { color: colors.success, children: ` ✓ ${label}` }, label))), cancelable ? _jsx(Text, { dimColor: true, children: ' ESC 取消' }) : null] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=ProgressDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProgressDialog.js","sourceRoot":"","sources":["../../src/components/ProgressDialog.tsx"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAmBnD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,KAAK,GAAG,EAAE;IACtD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,CAAA;IAC1C,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;AAC/D,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAa;IACzD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,GAAG,CAAA;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,KAAK,EACL,IAAI,EACJ,SAAS,GAAG,EAAE,EACd,MAAM,EACN,UAAU,GAAG,KAAK,EAClB,QAAQ,EACR,KAAK,GAAG,EAAE,GACU;IACpB,aAAa,CAAC,IAAI,CAAC,CAAA;IACnB,QAAQ,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;QACvB,IAAI,UAAU,IAAI,GAAG,CAAC,MAAM;YAAE,QAAQ,EAAE,EAAE,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC5C,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,WAAW,EAAC,OAAO,EAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,aACnG,KAAC,IAAI,IAAC,IAAI,kBAAE,IAAI,KAAK,EAAE,GAAQ,EAC/B,KAAC,IAAI,cAAE,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,KAAK,KAAK,OAAO,EAAE,GAAQ,EACnE,MAAM,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,QAAQ,kBAAE,IAAI,MAAM,EAAE,GAAQ,CAAC,CAAC,CAAC,IAAI,EACrD,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,YAAG,IAAI,WAAW,CAAC,OAAO,GAAG,GAAG,CAAC,IAAI,OAAO,GAAG,GAAQ,EAChF,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACxB,KAAC,IAAI,IAAa,KAAK,EAAE,MAAM,CAAC,OAAO,YAAG,MAAM,KAAK,EAAE,IAA5C,KAAK,CAA+C,CAChE,CAAC,EACD,UAAU,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,QAAQ,kBAAE,SAAS,GAAQ,CAAC,CAAC,CAAC,IAAI,IAClD,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* 可滚动列表。终端行数有限,几百个节点必须按视口裁剪,
|
|
4
|
+
* 否则 Ink 会把整个列表都画出来把屏幕顶飞。
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
/** 计算视口起始下标,让选中项尽量居中,且不越界 */
|
|
8
|
+
export function windowStart(selected, total, height) {
|
|
9
|
+
if (total <= height)
|
|
10
|
+
return 0;
|
|
11
|
+
const half = Math.floor(height / 2);
|
|
12
|
+
return Math.max(0, Math.min(selected - half, total - height));
|
|
13
|
+
}
|
|
14
|
+
export function ScrollList({ items, selected, height, renderItem, emptyText = '(空)', }) {
|
|
15
|
+
if (items.length === 0) {
|
|
16
|
+
return (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: emptyText }) }));
|
|
17
|
+
}
|
|
18
|
+
const rows = Math.max(1, height);
|
|
19
|
+
const start = windowStart(selected, items.length, rows);
|
|
20
|
+
const visible = items.slice(start, start + rows);
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", children: [visible.map((item, offset) => {
|
|
22
|
+
const index = start + offset;
|
|
23
|
+
return (_jsx(Box, { children: renderItem(item, index, index === selected) }, index));
|
|
24
|
+
}), items.length > rows && (_jsx(Text, { dimColor: true, children: ` ${start + 1}-${Math.min(start + rows, items.length)} / ${items.length}` }))] }));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ScrollList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScrollList.js","sourceRoot":"","sources":["../../src/components/ScrollList.tsx"],"names":[],"mappings":";AAAA;;;GAGG;AACH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAY/B,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,KAAa,EAAE,MAAc;IACzE,IAAI,KAAK,IAAI,MAAM;QAAE,OAAO,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACnC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,UAAU,CAAI,EAC5B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,SAAS,GAAG,KAAK,GACE;IACnB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CACL,KAAC,GAAG,cACF,KAAC,IAAI,IAAC,QAAQ,kBAAE,SAAS,GAAQ,GAC7B,CACP,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IAChC,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACvD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,CAAA;IAEhD,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;gBAC5B,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;gBAC5B,OAAO,CACL,KAAC,GAAG,cAAc,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,QAAQ,CAAC,IAAnD,KAAK,CAAqD,CACrE,CAAA;YACH,CAAC,CAAC,EACD,KAAK,CAAC,MAAM,GAAG,IAAI,IAAI,CACtB,KAAC,IAAI,IAAC,QAAQ,kBACX,KAAK,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,GACtE,CACR,IACG,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/** 底部状态栏:内核版本、实时流量、内存、连接状态(配色全部来自 theme)。
|
|
3
|
+
*
|
|
4
|
+
* 响应式降级(2026-09-22 标准化美化):
|
|
5
|
+
* - Full(≥100列):完整指标(版本、模式、瞬时、累计、内存、连接状态)
|
|
6
|
+
* - Compact(72~99列):精简指标(版本、模式、瞬时、连接状态;隐藏累计与内存)
|
|
7
|
+
* - TooSmall(<72列):不渲染状态栏(由 TopBar 显示降级提示)
|
|
8
|
+
*/
|
|
9
|
+
import { Box, Text, useStdout } from 'ink';
|
|
10
|
+
import { formatBytes } from '../commands/output.js';
|
|
11
|
+
import { StatusChip } from '../ui/StatusDot.js';
|
|
12
|
+
import { colors } from '../ui/theme.js';
|
|
13
|
+
import { classifySize } from '../ui/layout.js';
|
|
14
|
+
/** 连接状态 → 语义四档:断开是故障(红),重连/连接中是注意(黄),已连接是健康(绿) */
|
|
15
|
+
export function stateTone(state) {
|
|
16
|
+
switch (state) {
|
|
17
|
+
case 'open':
|
|
18
|
+
return 'positive';
|
|
19
|
+
case 'reconnecting':
|
|
20
|
+
case 'connecting':
|
|
21
|
+
return 'caution';
|
|
22
|
+
default:
|
|
23
|
+
return 'negative';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function stateLabel(state, nodeName) {
|
|
27
|
+
const nodeDisplay = nodeName ? `(${nodeName})` : '';
|
|
28
|
+
switch (state) {
|
|
29
|
+
case 'open':
|
|
30
|
+
return `已连接${nodeDisplay}`;
|
|
31
|
+
case 'reconnecting':
|
|
32
|
+
return '重连中';
|
|
33
|
+
case 'closed':
|
|
34
|
+
return '已断开';
|
|
35
|
+
default:
|
|
36
|
+
return '连接中';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function StatusBar(props) {
|
|
40
|
+
const { stdout } = useStdout();
|
|
41
|
+
const columns = stdout?.columns ?? 80;
|
|
42
|
+
const sizeClass = classifySize(columns);
|
|
43
|
+
// TooSmall 时不渲染状态栏(TopBar 已显示降级提示)
|
|
44
|
+
if (sizeClass === 'TooSmall') {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const showTotals = sizeClass === 'Full';
|
|
48
|
+
const showMem = sizeClass === 'Full';
|
|
49
|
+
const tone = stateTone(props.state);
|
|
50
|
+
const modeNames = {
|
|
51
|
+
rule: '规则',
|
|
52
|
+
global: '全局',
|
|
53
|
+
direct: '直连'
|
|
54
|
+
};
|
|
55
|
+
const modeDisplay = props.mode ? modeNames[props.mode] ?? props.mode : '?';
|
|
56
|
+
return (_jsx(Box, { borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: colors.surfaceBorder, paddingX: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: colors.muted, children: `mihomo ${props.version ?? '?'} │ ${modeDisplay} │ ` }), _jsxs(Text, { color: colors.success, children: ["\u2191", formatBytes(props.up), "/s"] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.success, children: ["\u2193", formatBytes(props.down), "/s"] }), showTotals ? (_jsx(Text, { color: colors.muted, children: ` │ 总 ↑${formatBytes(props.upTotal)} ↓${formatBytes(props.downTotal)}` })) : null, showMem ? _jsx(Text, { color: colors.muted, children: ` │ mem ${formatBytes(props.memory)}` }) : null, _jsx(Text, { color: colors.muted, children: ' │ ' }), _jsx(StatusChip, { tone: tone, children: stateLabel(props.state, sizeClass === 'Full' ? props.currentNode : '') })] }) }));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=StatusBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":";AAAA;;;;;;GAMG;AACH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEnD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,MAAM,EAAa,MAAM,gBAAgB,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAc9C,kDAAkD;AAClD,MAAM,UAAU,SAAS,CAAC,KAAkB;IAC1C,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,UAAU,CAAA;QACnB,KAAK,cAAc,CAAC;QACpB,KAAK,YAAY;YACf,OAAO,SAAS,CAAA;QAClB;YACE,OAAO,UAAU,CAAA;IACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAkB,EAAE,QAAgB;IAC7D,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IACnD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,MAAM,WAAW,EAAE,CAAA;QAC5B,KAAK,cAAc;YACjB,OAAO,KAAK,CAAA;QACd,KAAK,QAAQ;YACX,OAAO,KAAK,CAAA;QACd;YACE,OAAO,KAAK,CAAA;IAChB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAA;IAC9B,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,CAAA;IACrC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IAEvC,mCAAmC;IACnC,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,KAAK,MAAM,CAAA;IACvC,MAAM,OAAO,GAAG,SAAS,KAAK,MAAM,CAAA;IACpC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACnC,MAAM,SAAS,GAA2B;QACxC,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,IAAI;KACb,CAAA;IACD,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAA;IAE1E,OAAO,CACL,KAAC,GAAG,IACF,WAAW,EAAC,QAAQ,EACpB,SAAS,QACT,YAAY,EAAE,KAAK,EACnB,UAAU,EAAE,KAAK,EACjB,WAAW,EAAE,KAAK,EAClB,WAAW,EAAE,MAAM,CAAC,aAAa,EACjC,QAAQ,EAAE,CAAC,YAEX,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,YAAG,UAAU,KAAK,CAAC,OAAO,IAAI,GAAG,MAAM,WAAW,KAAK,GAAQ,EAExF,MAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,OAAO,uBAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,EAC9D,KAAC,IAAI,oBAAS,EACd,MAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,OAAO,uBAAI,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAC/D,UAAU,CAAC,CAAC,CAAC,CACZ,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,YACtB,SAAS,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,GAClE,CACR,CAAC,CAAC,CAAC,IAAI,EACP,OAAO,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,YAAG,UAAU,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC3F,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,YAAG,KAAK,GAAQ,EACzC,KAAC,UAAU,IAAC,IAAI,EAAE,IAAI,YAAG,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,GAAc,IACxG,GACH,CACP,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* 通用多行文本编辑器(v0.3.0 §7.4)。
|
|
4
|
+
*
|
|
5
|
+
* 与文件、订阅无关:调用方传入初始文本、校验函数与保存/放弃回调,
|
|
6
|
+
* 因此订阅 YAML、规则、本地节点等场景都能复用。
|
|
7
|
+
*
|
|
8
|
+
* 文档模型在 textEditor.ts(纯函数,按码点编辑);本组件只负责渲染、
|
|
9
|
+
* 键盘与粘贴绑定。ESC 有未保存修改时先弹确认再退出。
|
|
10
|
+
*
|
|
11
|
+
* 键位:方向键/Home/End 移动,字符输入,Backspace/Delete 删除,Enter 换行,
|
|
12
|
+
* Ctrl-S 校验后保存,ESC 放弃。
|
|
13
|
+
*/
|
|
14
|
+
import { useRef, useState } from 'react';
|
|
15
|
+
import { Box, Text, useInput, usePaste } from 'ink';
|
|
16
|
+
import { colors } from '../ui/theme.js';
|
|
17
|
+
import { Panel } from '../ui/Panel.js';
|
|
18
|
+
import { useKeyCapture } from '../ui/keyCapture.js';
|
|
19
|
+
import { ConfirmDialog } from './ConfirmDialog.js';
|
|
20
|
+
import { fromText, reduce, toText, visibleLines, } from './textEditorModel.js';
|
|
21
|
+
export function TextEditor({ title, initialText, height, width, validate, onSave, onCancel, }) {
|
|
22
|
+
const [state, setState] = useState(() => fromText(initialText));
|
|
23
|
+
const [error, setError] = useState(undefined);
|
|
24
|
+
const [confirmingCancel, setConfirmingCancel] = useState(false);
|
|
25
|
+
const stateRef = useRef(state);
|
|
26
|
+
stateRef.current = state;
|
|
27
|
+
// 可视行数:面板顶线 1 + 底栏 1 + 边框余量
|
|
28
|
+
const viewHeight = Math.max(1, height - 4);
|
|
29
|
+
const gutterWidth = String(state.lines.length).length + 1;
|
|
30
|
+
const textWidth = Math.max(1, width - gutterWidth - 4);
|
|
31
|
+
const dirty = toText(state) !== initialText.replace(/\r\n/g, '\n');
|
|
32
|
+
useKeyCapture(true);
|
|
33
|
+
usePaste((text) => {
|
|
34
|
+
setState((prev) => reduce(prev, { type: 'insert', text }, viewHeight));
|
|
35
|
+
setError(undefined);
|
|
36
|
+
});
|
|
37
|
+
useInput((input, key) => {
|
|
38
|
+
if (confirmingCancel)
|
|
39
|
+
return;
|
|
40
|
+
if (key.escape) {
|
|
41
|
+
if (dirty)
|
|
42
|
+
setConfirmingCancel(true);
|
|
43
|
+
else
|
|
44
|
+
onCancel();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Ctrl-S 保存
|
|
48
|
+
if (key.ctrl && input === 's') {
|
|
49
|
+
const text = toText(stateRef.current);
|
|
50
|
+
const problem = validate?.(text);
|
|
51
|
+
if (problem) {
|
|
52
|
+
setError(problem);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
onSave(text);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const action = keyAction(input, key);
|
|
59
|
+
if (!action)
|
|
60
|
+
return;
|
|
61
|
+
setState((prev) => reduce(prev, action, viewHeight));
|
|
62
|
+
setError(undefined);
|
|
63
|
+
});
|
|
64
|
+
if (confirmingCancel) {
|
|
65
|
+
return (_jsx(ConfirmDialog, { message: ['放弃未保存的修改?', '已编辑的内容将丢失'], onConfirm: onCancel, onCancel: () => setConfirmingCancel(false) }));
|
|
66
|
+
}
|
|
67
|
+
const lines = visibleLines(state, viewHeight, textWidth);
|
|
68
|
+
const status = error
|
|
69
|
+
? error
|
|
70
|
+
: dirty
|
|
71
|
+
? `已修改 · ${state.lines.length} 行`
|
|
72
|
+
: `${state.lines.length} 行`;
|
|
73
|
+
return (_jsx(Panel, { title: title, width: width, fillHeight: true, children: _jsxs(Box, { flexDirection: "column", children: [lines.map((line) => (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: `${String(line.index + 1).padStart(gutterWidth - 1)} ` }), _jsx(Text, { children: renderLine(line.text, line.cursor ? line.cursorOffset : -1) })] }, line.index))), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: error ? colors.danger : colors.muted, children: status }), _jsx(Text, { dimColor: true, children: ' Ctrl-S 保存 ESC 放弃' })] })] }) }));
|
|
74
|
+
}
|
|
75
|
+
/** 光标位置反色显示;光标在行尾时补一个空块 */
|
|
76
|
+
function renderLine(text, cursorOffset) {
|
|
77
|
+
if (cursorOffset < 0)
|
|
78
|
+
return text;
|
|
79
|
+
const chars = [...text];
|
|
80
|
+
const before = chars.slice(0, cursorOffset).join('');
|
|
81
|
+
const at = chars[cursorOffset] ?? ' ';
|
|
82
|
+
const after = chars.slice(cursorOffset + 1).join('');
|
|
83
|
+
return (_jsxs(_Fragment, { children: [before, _jsx(Text, { inverse: true, children: at }), after] }));
|
|
84
|
+
}
|
|
85
|
+
function keyAction(input, key) {
|
|
86
|
+
if (key.upArrow)
|
|
87
|
+
return { type: 'move', dir: 'up' };
|
|
88
|
+
if (key.downArrow)
|
|
89
|
+
return { type: 'move', dir: 'down' };
|
|
90
|
+
if (key.leftArrow)
|
|
91
|
+
return { type: 'move', dir: 'left' };
|
|
92
|
+
if (key.rightArrow)
|
|
93
|
+
return { type: 'move', dir: 'right' };
|
|
94
|
+
if (key.ctrl && input === 'a')
|
|
95
|
+
return { type: 'move', dir: 'home' };
|
|
96
|
+
if (key.ctrl && input === 'e')
|
|
97
|
+
return { type: 'move', dir: 'end' };
|
|
98
|
+
if (key.return)
|
|
99
|
+
return { type: 'insert', text: '\n' };
|
|
100
|
+
if (key.backspace)
|
|
101
|
+
return { type: 'backspace' };
|
|
102
|
+
if (key.delete)
|
|
103
|
+
return { type: 'delete' };
|
|
104
|
+
if (key.ctrl || input.length === 0)
|
|
105
|
+
return undefined;
|
|
106
|
+
// 过滤控制字符,其余(含粘贴兜底的多字符)按插入处理
|
|
107
|
+
const cleaned = [...input].filter((char) => char >= ' ' && char !== '\x7f').join('');
|
|
108
|
+
if (!cleaned)
|
|
109
|
+
return undefined;
|
|
110
|
+
return { type: 'insert', text: cleaned };
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=TextEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextEditor.js","sourceRoot":"","sources":["../../src/components/TextEditor.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 多行文本编辑器的文档模型(v0.3.0 §7.3)。纯逻辑,不依赖 Ink。
|
|
3
|
+
*
|
|
4
|
+
* 行内位置一律按码点(code point)计,emoji 等代理对不会被切断。
|
|
5
|
+
* 滚动语义借鉴 mihari viewport.go 的 EnsureLineVisible:光标行始终
|
|
6
|
+
* 保持在可视窗口内,窗口不越界。
|
|
7
|
+
*/
|
|
8
|
+
import { inputWindow } from './InputDialog.js';
|
|
9
|
+
/** 把文本拆成行;空串得到一个空行,便于光标落位 */
|
|
10
|
+
export function fromText(text) {
|
|
11
|
+
const normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
12
|
+
const lines = normalized.split('\n');
|
|
13
|
+
return { lines: lines.length > 0 ? lines : [''], row: 0, col: 0, scroll: 0 };
|
|
14
|
+
}
|
|
15
|
+
export function toText(state) {
|
|
16
|
+
return state.lines.join('\n');
|
|
17
|
+
}
|
|
18
|
+
const chars = (line) => [...line];
|
|
19
|
+
/**
|
|
20
|
+
* 归约一次编辑动作。viewHeight 是可视行数,用于在动作后把光标行
|
|
21
|
+
* 滚进窗口;传 0 表示不滚动。
|
|
22
|
+
*/
|
|
23
|
+
export function reduce(state, action, viewHeight = 0) {
|
|
24
|
+
const next = applyAction(state, action);
|
|
25
|
+
return viewHeight > 0 ? scrollToCursor(next, viewHeight) : next;
|
|
26
|
+
}
|
|
27
|
+
function applyAction(state, action) {
|
|
28
|
+
switch (action.type) {
|
|
29
|
+
case 'insert':
|
|
30
|
+
return insertText(state, action.text);
|
|
31
|
+
case 'backspace':
|
|
32
|
+
return backspace(state);
|
|
33
|
+
case 'delete':
|
|
34
|
+
return deleteForward(state);
|
|
35
|
+
case 'move':
|
|
36
|
+
return move(state, action.dir);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** 插入文本,可含换行(粘贴多行一次完成) */
|
|
40
|
+
function insertText(state, text) {
|
|
41
|
+
const lines = state.lines.slice();
|
|
42
|
+
const current = chars(lines[state.row] ?? '');
|
|
43
|
+
const before = current.slice(0, state.col).join('');
|
|
44
|
+
const after = current.slice(state.col).join('');
|
|
45
|
+
const inserted = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
46
|
+
const first = inserted[0] ?? '';
|
|
47
|
+
const last = inserted[inserted.length - 1] ?? '';
|
|
48
|
+
lines.splice(state.row, 1, before + first, ...inserted.slice(1, -1), last + after);
|
|
49
|
+
// 上面在 inserted 只有一段时会重复,收敛掉
|
|
50
|
+
if (inserted.length === 1) {
|
|
51
|
+
lines.splice(state.row, 2, before + first + after);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
...state,
|
|
55
|
+
lines,
|
|
56
|
+
row: state.row + inserted.length - 1,
|
|
57
|
+
col: [...(inserted.length === 1 ? before + first : last)].length,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** 退格:行内删一个码点;行首则把当前行并入上一行 */
|
|
61
|
+
function backspace(state) {
|
|
62
|
+
const lines = state.lines.slice();
|
|
63
|
+
const current = chars(lines[state.row] ?? '');
|
|
64
|
+
if (state.col > 0) {
|
|
65
|
+
lines[state.row] = [...current.slice(0, state.col - 1), ...current.slice(state.col)].join('');
|
|
66
|
+
return { ...state, lines, col: state.col - 1 };
|
|
67
|
+
}
|
|
68
|
+
if (state.row === 0)
|
|
69
|
+
return state;
|
|
70
|
+
const prev = lines[state.row - 1] ?? '';
|
|
71
|
+
lines.splice(state.row - 1, 2, prev + current.join(''));
|
|
72
|
+
return { ...state, lines, row: state.row - 1, col: [...prev].length };
|
|
73
|
+
}
|
|
74
|
+
/** 向前删除:行内删光标后一个码点;行尾则合并下一行 */
|
|
75
|
+
function deleteForward(state) {
|
|
76
|
+
const lines = state.lines.slice();
|
|
77
|
+
const current = chars(lines[state.row] ?? '');
|
|
78
|
+
if (state.col < current.length) {
|
|
79
|
+
lines[state.row] = [...current.slice(0, state.col), ...current.slice(state.col + 1)].join('');
|
|
80
|
+
return { ...state, lines };
|
|
81
|
+
}
|
|
82
|
+
if (state.row >= lines.length - 1)
|
|
83
|
+
return state;
|
|
84
|
+
lines.splice(state.row, 2, current.join('') + (lines[state.row + 1] ?? ''));
|
|
85
|
+
return { ...state, lines };
|
|
86
|
+
}
|
|
87
|
+
function move(state, dir) {
|
|
88
|
+
const lineLen = chars(state.lines[state.row] ?? '').length;
|
|
89
|
+
switch (dir) {
|
|
90
|
+
case 'left':
|
|
91
|
+
if (state.col > 0)
|
|
92
|
+
return { ...state, col: state.col - 1 };
|
|
93
|
+
if (state.row === 0)
|
|
94
|
+
return state;
|
|
95
|
+
return { ...state, row: state.row - 1, col: chars(state.lines[state.row - 1] ?? '').length };
|
|
96
|
+
case 'right':
|
|
97
|
+
if (state.col < lineLen)
|
|
98
|
+
return { ...state, col: state.col + 1 };
|
|
99
|
+
if (state.row >= state.lines.length - 1)
|
|
100
|
+
return state;
|
|
101
|
+
return { ...state, row: state.row + 1, col: 0 };
|
|
102
|
+
case 'up':
|
|
103
|
+
return moveVertical(state, -1);
|
|
104
|
+
case 'down':
|
|
105
|
+
return moveVertical(state, 1);
|
|
106
|
+
case 'home':
|
|
107
|
+
return { ...state, col: 0 };
|
|
108
|
+
case 'end':
|
|
109
|
+
return { ...state, col: lineLen };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** 上下移动时列号尽量保持,目标行更短则落到行尾 */
|
|
113
|
+
function moveVertical(state, delta) {
|
|
114
|
+
const row = Math.min(Math.max(0, state.row + delta), state.lines.length - 1);
|
|
115
|
+
const len = chars(state.lines[row] ?? '').length;
|
|
116
|
+
return { ...state, row, col: Math.min(state.col, len) };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 让光标行保持在可视窗口内(借鉴 mihari EnsureLineVisible)。
|
|
120
|
+
* 光标在窗口之上 → 窗口上移到光标行;在窗口之下 → 窗口下移使光标落在末行。
|
|
121
|
+
*/
|
|
122
|
+
export function scrollToCursor(state, viewHeight) {
|
|
123
|
+
if (viewHeight <= 0)
|
|
124
|
+
return state;
|
|
125
|
+
const maxScroll = Math.max(0, state.lines.length - viewHeight);
|
|
126
|
+
let scroll = Math.min(state.scroll, maxScroll);
|
|
127
|
+
if (state.row < scroll)
|
|
128
|
+
scroll = state.row;
|
|
129
|
+
if (state.row >= scroll + viewHeight)
|
|
130
|
+
scroll = state.row - viewHeight + 1;
|
|
131
|
+
return { ...state, scroll: Math.min(Math.max(0, scroll), maxScroll) };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 取可视窗口内的行。每行按 maxWidth 做横向开窗:光标行以光标为中心,
|
|
135
|
+
* 其余行显示开头。复用 InputDialog 的 inputWindow。
|
|
136
|
+
*/
|
|
137
|
+
export function visibleLines(state, viewHeight, maxWidth) {
|
|
138
|
+
const start = state.scroll;
|
|
139
|
+
const end = Math.min(state.lines.length, start + Math.max(0, viewHeight));
|
|
140
|
+
const result = [];
|
|
141
|
+
for (let index = start; index < end; index++) {
|
|
142
|
+
const line = state.lines[index] ?? '';
|
|
143
|
+
const isCursor = index === state.row;
|
|
144
|
+
const windowed = inputWindow(line, isCursor ? state.col : 0, maxWidth);
|
|
145
|
+
result.push({
|
|
146
|
+
index,
|
|
147
|
+
text: windowed.text,
|
|
148
|
+
cursor: isCursor,
|
|
149
|
+
cursorOffset: isCursor ? windowed.cursorOffset : -1,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=textEditorModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textEditorModel.js","sourceRoot":"","sources":["../../src/components/textEditorModel.ts"],"names":[],"mappings":"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"}
|