screen-manager-tui 1.0.0 → 1.1.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/README.md +22 -5
- package/dist/utils/dirs.js +4 -45
- package/dist/utils/screen.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,24 +17,32 @@ SSH 登录后的交互式 GNU Screen 会话管理器。基于 Ink (React for CLI
|
|
|
17
17
|
|
|
18
18
|
## 安装
|
|
19
19
|
|
|
20
|
+
推荐通过 npm 全局安装:
|
|
21
|
+
|
|
20
22
|
```bash
|
|
21
23
|
npm install -g screen-manager-tui
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
安装后即可使用 `sm` 命令。
|
|
27
|
+
|
|
28
|
+
<details>
|
|
29
|
+
<summary>从源码安装</summary>
|
|
25
30
|
|
|
26
31
|
```bash
|
|
27
|
-
|
|
32
|
+
git clone https://github.com/m2kar/sm.git
|
|
33
|
+
cd sm
|
|
28
34
|
npm install
|
|
29
35
|
npm run build
|
|
30
|
-
|
|
36
|
+
npm link
|
|
31
37
|
```
|
|
32
38
|
|
|
39
|
+
</details>
|
|
40
|
+
|
|
33
41
|
## 使用
|
|
34
42
|
|
|
35
43
|
```bash
|
|
36
44
|
sm # 启动
|
|
37
|
-
SM_HOME=/home/zhiqing sm #
|
|
45
|
+
SM_HOME=/home/zhiqing sm # 指定项目根目录
|
|
38
46
|
```
|
|
39
47
|
|
|
40
48
|
### 首页
|
|
@@ -84,7 +92,7 @@ SM_HOME=/home/zhiqing sm # 指定项目���目录
|
|
|
84
92
|
|
|
85
93
|
```
|
|
86
94
|
SSH 登录 → sm 启动 → 选择/新建会话 → 进入 screen
|
|
87
|
-
↑
|
|
95
|
+
↑ ↓
|
|
88
96
|
└──── Ctrl-A D 从 screen 断开 ────────┘
|
|
89
97
|
按 q → 退出到 Shell
|
|
90
98
|
```
|
|
@@ -114,6 +122,15 @@ fi
|
|
|
114
122
|
- `$STY` — 已在 screen 中则跳过
|
|
115
123
|
- `$SM_SKIP=1` — 临时跳过 sm
|
|
116
124
|
|
|
125
|
+
## 发版
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm version patch # 或 minor / major
|
|
129
|
+
git push && git push --tags
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
推送 `v*` tag 后,GitHub Actions 会自动通过 Trusted Publisher 发布到 npm。
|
|
133
|
+
|
|
117
134
|
## 技术栈
|
|
118
135
|
|
|
119
136
|
- [Ink 7](https://github.com/vadimdemedes/ink) (React for CLI) + React 19 + TypeScript
|
package/dist/utils/dirs.js
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, writeFileSync, statSync } from 'node:fs';
|
|
2
2
|
import { join, basename, sep } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
const
|
|
5
|
-
'miniconda3',
|
|
6
|
-
'anaconda3',
|
|
7
|
-
'.nvm',
|
|
8
|
-
'.npm',
|
|
9
|
-
'.cache',
|
|
10
|
-
'.local',
|
|
11
|
-
'.config',
|
|
12
|
-
'.oh-my-zsh',
|
|
13
|
-
'.claude',
|
|
14
|
-
'node_modules',
|
|
15
|
-
'.vscode-server',
|
|
16
|
-
'.cursor-server',
|
|
17
|
-
]);
|
|
4
|
+
const MAX_FAVORITES = 10;
|
|
18
5
|
export function getHome() {
|
|
19
6
|
return process.env.SM_HOME || homedir();
|
|
20
7
|
}
|
|
@@ -113,13 +100,10 @@ export function shortenPath(fullPath, maxLen) {
|
|
|
113
100
|
const result = sep + abbreviated.join(sep) + sep + last;
|
|
114
101
|
return result.length <= maxLen ? result : last;
|
|
115
102
|
}
|
|
116
|
-
// --- Project directory listing ---
|
|
103
|
+
// --- Project directory listing (favorites only) ---
|
|
117
104
|
export function getProjectDirs() {
|
|
118
|
-
const
|
|
119
|
-
const favorites = loadFavorites();
|
|
120
|
-
const favPaths = new Set(favorites.map((f) => f.path));
|
|
105
|
+
const favorites = loadFavorites().slice(0, MAX_FAVORITES);
|
|
121
106
|
const dirs = [];
|
|
122
|
-
// Favorites first, sorted by lastUsed (already sorted)
|
|
123
107
|
for (const fav of favorites) {
|
|
124
108
|
try {
|
|
125
109
|
statSync(fav.path);
|
|
@@ -129,31 +113,6 @@ export function getProjectDirs() {
|
|
|
129
113
|
// skip non-existent
|
|
130
114
|
}
|
|
131
115
|
}
|
|
132
|
-
// Home subdirectories (excluding favorites)
|
|
133
|
-
try {
|
|
134
|
-
const entries = readdirSync(home, { withFileTypes: true });
|
|
135
|
-
for (const entry of entries) {
|
|
136
|
-
if (!entry.isDirectory())
|
|
137
|
-
continue;
|
|
138
|
-
if (entry.name.startsWith('.'))
|
|
139
|
-
continue;
|
|
140
|
-
if (EXCLUDED_DIRS.has(entry.name))
|
|
141
|
-
continue;
|
|
142
|
-
const fullPath = join(home, entry.name);
|
|
143
|
-
if (favPaths.has(fullPath))
|
|
144
|
-
continue;
|
|
145
|
-
try {
|
|
146
|
-
statSync(fullPath);
|
|
147
|
-
dirs.push({ name: entry.name, path: fullPath });
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
// skip
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
// ignore
|
|
156
|
-
}
|
|
157
116
|
return dirs;
|
|
158
117
|
}
|
|
159
118
|
export function touchFavoriteBySessionName(sessionName) {
|
package/dist/utils/screen.js
CHANGED
|
@@ -4,13 +4,15 @@ export function listSessions() {
|
|
|
4
4
|
const result = spawnSync('screen', ['-ls'], { encoding: 'utf-8' });
|
|
5
5
|
const output = result.stdout || result.stderr || '';
|
|
6
6
|
const sessions = [];
|
|
7
|
-
|
|
7
|
+
// Linux: \t12345.name\t(03/15/2025 10:30:00 AM)\t(Detached)
|
|
8
|
+
// macOS: \t3396.name\t(Detached)
|
|
9
|
+
const regex = /^\t(\d+)\.(.+?)\t(?:\((.+?)\)\t)?\((Attached|Detached|Dead)\)/gm;
|
|
8
10
|
let match;
|
|
9
11
|
while ((match = regex.exec(output)) !== null) {
|
|
10
12
|
sessions.push({
|
|
11
13
|
pid: parseInt(match[1], 10),
|
|
12
14
|
name: match[2],
|
|
13
|
-
date: match[3],
|
|
15
|
+
date: match[3] || '',
|
|
14
16
|
status: match[4],
|
|
15
17
|
});
|
|
16
18
|
}
|