@wendongfly/myhi 1.0.6 → 1.0.8

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/bin/myhi.js CHANGED
@@ -7,7 +7,22 @@ import { homedir } from 'os';
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = dirname(__filename);
10
- const cmd = process.argv[2];
10
+
11
+ // 解析 -p / --port 参数
12
+ const args = process.argv.slice(2);
13
+ let cmd = null;
14
+ for (let i = 0; i < args.length; i++) {
15
+ if ((args[i] === '-p' || args[i] === '--port') && args[i + 1]) {
16
+ process.env.PORT = args[i + 1];
17
+ args.splice(i, 2); i--;
18
+ } else if (args[i].startsWith('--port=')) {
19
+ process.env.PORT = args[i].split('=')[1];
20
+ args.splice(i, 1); i--;
21
+ } else if (!cmd && !args[i].startsWith('-')) {
22
+ cmd = args[i];
23
+ }
24
+ }
25
+ if (!cmd) cmd = args[0];
11
26
 
12
27
  const configDir = join(homedir(), '.myhi');
13
28
  const pidFile = join(configDir, 'daemon.pid');
@@ -90,12 +105,13 @@ if (cmd === 'attach') {
90
105
 
91
106
  } else if (cmd === 'help' || cmd === '--help' || cmd === '-h') {
92
107
  console.log(`
93
- myhi v1.0.0 — 基于 Web 的终端共享工具
108
+ myhi — 基于 Web 的终端共享工具
94
109
  通过局域网或 Tailscale 在手机/平板上控制电脑终端,无需中继服务器。
95
110
 
96
111
  服务器:
97
112
  myhi 前台启动服务器
98
113
  myhi start 前台启动服务器
114
+ myhi -p <port> 指定监听端口(默认 3000)
99
115
  myhi -d, --daemon 后台启动服务器(日志写入 ~/.myhi/daemon.log)
100
116
  myhi stop 停止后台服务器
101
117
  myhi restart 重启后台服务器
@@ -110,10 +126,12 @@ myhi v1.0.0 — 基于 Web 的终端共享工具
110
126
  快捷键(attach 模式):
111
127
  Ctrl+] 分离终端(退出但不关闭会话)
112
128
 
129
+ 选项:
130
+ -p, --port <port> 监听端口(默认 3000)
131
+
113
132
  环境变量:
114
133
  PORT 监听端口(默认 3000)
115
134
  HOST 监听地址(默认 0.0.0.0)
116
- HTTPS_PORT HTTPS 端口(默认 3443)
117
135
  MYHI_AUTO_ATTACH 启动时自动创建会话(默认 1,设 0 关闭)
118
136
  MYHI_CWD 会话工作目录(默认当前目录)
119
137
  MYHI_SERVER attach 连接的服务器地址(默认 http://localhost:3000)
@@ -128,7 +146,8 @@ myhi v1.0.0 — 基于 Web 的终端共享工具
128
146
 
129
147
  示例:
130
148
  myhi # 启动后扫描二维码即可在手机上操作
131
- PORT=8080 myhi -d # 8080 端口后台启动
149
+ myhi -p 8080 # 指定 8080 端口启动
150
+ myhi -p 8080 -d # 8080 端口后台启动
132
151
  MYHI_CWD=/project myhi # 指定会话工作目录
133
152
  myhi attach # 从另一台机器连接到会话
134
153
  `);
package/dist/chat.html CHANGED
@@ -129,7 +129,13 @@
129
129
  /* ── 输入栏 ─────────────────────────────── */
130
130
  #input-area { background: #161b22; border-top: 1px solid #30363d; flex-shrink: 0; padding: 0.5rem 0.75rem 0.35rem; }
131
131
  #input-area.no-shortcuts { padding-bottom: max(0.5rem, env(safe-area-inset-bottom)); }
132
- #input-box { background: #0d1117; border: 1px solid #30363d; border-radius: 12px; transition: border-color 0.2s; overflow: hidden; }
132
+ #input-box { background: #0d1117; border: 1px solid #30363d; border-radius: 12px; transition: border-color 0.2s; overflow: hidden; position: relative; }
133
+ #img-preview { display: none; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; border-bottom: 1px solid #21262d; background: #161b22; }
134
+ #img-preview.show { display: flex; }
135
+ #img-preview img { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; border: 1px solid #30363d; }
136
+ #img-preview .img-info { flex: 1; font-size: 0.78rem; color: #8b949e; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
137
+ #img-preview .img-remove { width: 24px; height: 24px; border-radius: 50%; border: none; background: #21262d; color: #8b949e; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }
138
+ #img-preview .img-remove:hover { background: #f85149; color: #fff; }
133
139
  #input-box.focused { border-color: #58a6ff; }
134
140
  #input-box.disabled { opacity: 0.5; pointer-events: none; }
135
141
  #cmd-input { display: block; width: 100%; background: transparent; color: #e6edf3; border: none; padding: 0.6rem 0.75rem 0.3rem; font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; font-size: 0.85rem; outline: none; resize: none; line-height: 1.4; max-height: 120px; overflow-y: auto; }
@@ -223,6 +229,11 @@
223
229
  <div id="input-area">
224
230
  <div id="readonly-overlay">只读模式 — 点击状态栏"获取控制"开始输入</div>
225
231
  <div id="input-box">
232
+ <div id="img-preview">
233
+ <img id="img-preview-thumb" src="" alt="">
234
+ <span class="img-info">已添加图片,输入文字后发送</span>
235
+ <button class="img-remove" onclick="clearPendingImage()" title="移除图片">&times;</button>
236
+ </div>
226
237
  <textarea id="cmd-input" rows="1" placeholder="输入消息..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
227
238
  <div id="input-toolbar">
228
239
  <button class="tb-btn" id="btn-photo" onclick="openCamera()" title="拍照">
@@ -997,11 +1008,30 @@
997
1008
  setWorkState('idle');
998
1009
  });
999
1010
 
1011
+ // ── 待发送图片 ──────────────────────────────────
1012
+ let _pendingImage = null; // { path, blobUrl }
1013
+
1014
+ window.clearPendingImage = function() {
1015
+ if (_pendingImage?.blobUrl) URL.revokeObjectURL(_pendingImage.blobUrl);
1016
+ _pendingImage = null;
1017
+ document.getElementById('img-preview').classList.remove('show');
1018
+ cmdInput.placeholder = currentSession?.mode === 'agent' ? '输入消息...' : '输入命令...';
1019
+ };
1020
+
1021
+ function setPendingImage(path, blobUrl) {
1022
+ _pendingImage = { path, blobUrl };
1023
+ const preview = document.getElementById('img-preview');
1024
+ document.getElementById('img-preview-thumb').src = blobUrl || '';
1025
+ preview.classList.add('show');
1026
+ cmdInput.placeholder = '输入文字描述后发送,或直接发送分析图片...';
1027
+ cmdInput.focus();
1028
+ }
1029
+
1000
1030
  // ── 输入处理 ──────────────────────────────────
1001
1031
  window.sendCommand = sendCommand;
1002
1032
  function sendCommand() {
1003
- const text = cmdInput.value;
1004
- if (!text) return;
1033
+ const text = cmdInput.value.trim();
1034
+ if (!text && !_pendingImage) return;
1005
1035
 
1006
1036
  // 命名模式:发送的是新会话名称
1007
1037
  if (window._renameMode) {
@@ -1017,33 +1047,30 @@
1017
1047
  if (!isController) { addStatusMessage('请先获取控制权'); return; }
1018
1048
  endStream();
1019
1049
 
1020
- // 提取图片路径标记 [图片:xxx]
1021
- const imgMatch = text.match(/^\[图片:(.+?)\]\s*/);
1022
- const imgPath = imgMatch ? imgMatch[1] : null;
1023
- const userText = imgMatch ? text.slice(imgMatch[0].length).trim() : text;
1050
+ const imgPath = _pendingImage?.path || null;
1051
+ const userText = text;
1024
1052
  const displayText = imgPath ? `📷 ${userText || '分析图片'}` : text;
1025
1053
 
1026
1054
  addInputMessage(displayText);
1027
1055
 
1028
1056
  if (currentSession?.mode === 'agent') {
1029
- // Agent 模式:组合图片+文字
1030
1057
  const prompt = imgPath
1031
1058
  ? `请直接用 Read 工具读取图片 ${imgPath} 并分析,不要搜索其他文件。${userText || '描述图片内容'}`
1032
1059
  : text;
1033
1060
  socket.emit('agent:query', { prompt });
1034
1061
  showThinking();
1035
1062
  } else {
1036
- // PTY 模式:如果有图片,发送路径+用户文字
1037
1063
  const input = imgPath
1038
1064
  ? (userText ? `${userText} ${imgPath}` : imgPath)
1039
1065
  : text;
1040
1066
  socket.emit('input', input + '\r');
1041
1067
  }
1042
- cmdHistory.unshift(text);
1068
+ cmdHistory.unshift(imgPath ? `[图片] ${userText}` : text);
1043
1069
  if (cmdHistory.length > 100) cmdHistory.pop();
1044
1070
  historyIdx = -1;
1045
1071
  cmdInput.value = '';
1046
1072
  cmdInput.style.height = 'auto';
1073
+ clearPendingImage();
1047
1074
  cmdInput.focus();
1048
1075
  }
1049
1076
 
@@ -1310,10 +1337,9 @@
1310
1337
  const form = new FormData(); form.append('image', blob, 'photo.jpg');
1311
1338
  const res = await fetch(`/upload?sessionId=${SESSION_ID}`, { method: 'POST', body: form }); const data = await res.json();
1312
1339
  if (data.path) {
1313
- // 如果输入框有内容,带上用户描述一起发送;否则直接分析图片
1314
- const userDesc = cmdInput.value.trim();
1315
- cmdInput.value = `[图片:${data.path}] ${userDesc}`;
1316
- sendCommand();
1340
+ // 暂存图片,等用户输入文字后一起发送
1341
+ const thumbUrl = URL.createObjectURL(blob);
1342
+ setPendingImage(data.path, thumbUrl);
1317
1343
  }
1318
1344
  else addStatusMessage('上传失败: ' + (data.error || '未知'));
1319
1345
  } catch (e) { addStatusMessage('出错: ' + e.message); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/myhi",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Web-based terminal sharing with chat UI — control your terminal from phone via LAN/Tailscale",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",