@wendongfly/myhi 1.3.102 → 1.3.104

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
@@ -11,6 +11,7 @@ const __dirname = dirname(__filename);
11
11
  // 解析 -p / --port 参数
12
12
  const args = process.argv.slice(2);
13
13
  let cmd = null;
14
+ let httpOnlyFlag = null; // null=不改;true=切 HTTP-only;false=切回 HTTPS
14
15
  for (let i = 0; i < args.length; i++) {
15
16
  if ((args[i] === '-p' || args[i] === '--port') && args[i + 1]) {
16
17
  process.env.PORT = args[i + 1];
@@ -18,6 +19,10 @@ for (let i = 0; i < args.length; i++) {
18
19
  } else if (args[i].startsWith('--port=')) {
19
20
  process.env.PORT = args[i].split('=')[1];
20
21
  args.splice(i, 1); i--;
22
+ } else if (args[i] === '--http' || args[i] === '--no-https') {
23
+ httpOnlyFlag = true; args.splice(i, 1); i--;
24
+ } else if (args[i] === '--https') {
25
+ httpOnlyFlag = false; args.splice(i, 1); i--;
21
26
  } else if (!cmd && !args[i].startsWith('-')) {
22
27
  cmd = args[i];
23
28
  }
@@ -39,6 +44,24 @@ if (cmd === '--version' || cmd === '-v' || cmd === 'version') {
39
44
  const configDir = join(homedir(), '.myhi');
40
45
  const pidFile = join(configDir, 'daemon.pid');
41
46
  const logFile = join(configDir, 'daemon.log');
47
+ const configFile = join(configDir, 'config.json');
48
+
49
+ // --http / --https:持久化 HTTP-only 开关到 config.json,server 启动时读取(任何重启都保留,
50
+ // 不依赖环境变量从 daemon 往下传)。设完若没显式命令,就自动 restart 让它立刻生效。
51
+ if (httpOnlyFlag !== null) {
52
+ try {
53
+ mkdirSync(configDir, { recursive: true });
54
+ let cfg = {};
55
+ try { cfg = JSON.parse(readFileSync(configFile, 'utf8')); } catch {}
56
+ cfg.httpOnly = httpOnlyFlag;
57
+ writeFileSync(configFile, JSON.stringify(cfg, null, 2));
58
+ console.log(`[myhi] 已切换为 ${httpOnlyFlag ? 'HTTP(无 TLS,麦克风/摄像头等在非 localhost 会受限)' : 'HTTPS'} 模式`);
59
+ } catch (e) {
60
+ console.error('[myhi] 写入 config.json 失败:', e.message);
61
+ process.exit(1);
62
+ }
63
+ if (!cmd) cmd = 'restart'; // 无显式命令 → 切完直接重启使其生效
64
+ }
42
65
 
43
66
  // ── 工具函数 ──────────────────────────────────────────────
44
67
 
@@ -585,6 +608,8 @@ myhi — 基于 Web 的终端共享工具 (${platform})
585
608
  myhi start 前台启动服务器
586
609
  myhi -p <port> 指定监听端口(默认 12300)
587
610
  myhi -d 后台启动(日志写入 ${configPath}daemon.log)
611
+ myhi --http 切为纯 HTTP(无 TLS)并重启,持久生效
612
+ myhi --https 切回 HTTPS(默认)并重启
588
613
  myhi stop 停止后台服务器
589
614
  myhi restart 重启后台服务器
590
615
  myhi status 查看运行状态
package/dist/chat.html CHANGED
@@ -352,6 +352,7 @@
352
352
  <button class="tb-btn" id="btn-photo" onclick="openCamera()" title="拍照">
353
353
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/></svg>
354
354
  </button>
355
+ <span id="enter-hint" style="display:none;font-size:0.68rem;color:var(--muted);white-space:nowrap;align-self:center;margin-left:0.15rem"></span>
355
356
  <div class="spacer"></div>
356
357
  <button class="tb-btn" id="voice-btn" onclick="openVaSheet()" title="语音输入 / 需求助手">
357
358
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z"/><path d="M19 10v2a7 7 0 01-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
@@ -453,6 +454,18 @@
453
454
  <label class="setting-toggle"><span>显示思考过程</span><input type="checkbox" id="set-thinking" checked></label>
454
455
  <label class="setting-toggle"><span>工具调用默认折叠</span><input type="checkbox" id="set-collapse" checked></label>
455
456
  </div>
457
+ <div class="setting-group">
458
+ <h4>输入</h4>
459
+ <div style="font-size:0.8rem;color:var(--text2);margin:0.1rem 0 0.35rem">回车键</div>
460
+ <div id="seg-enter" class="seg-row">
461
+ <button class="seg" data-enter="auto">自动</button>
462
+ <button class="seg" data-enter="send">发送</button>
463
+ <button class="seg" data-enter="newline">换行</button>
464
+ </div>
465
+ <div style="font-size:0.72rem;color:var(--muted);margin-top:0.35rem;line-height:1.5">
466
+ 自动:电脑回车发送 / Shift+回车换行;手机回车换行、用按钮发送。<br>发送:回车总是发送(换行用 Shift+回车)。换行:回车总是换行(发送用 Ctrl/⌘+回车 或按钮)。
467
+ </div>
468
+ </div>
456
469
  <div class="setting-group">
457
470
  <h4>语音识别 (ASR)</h4>
458
471
  <div style="font-size:0.72rem;color:var(--muted);margin-bottom:0.35rem">SenseVoice 或兼容服务的地址,留空表示禁用语音</div>
@@ -808,7 +821,7 @@
808
821
  let workState = 'idle';
809
822
 
810
823
  // 设置
811
- const DEFAULT_SETTINGS = { parseOutput: true, showThinking: true, collapseTools: true };
824
+ const DEFAULT_SETTINGS = { parseOutput: true, showThinking: true, collapseTools: true, enterMode: 'auto' };
812
825
  let settings = loadSettings();
813
826
 
814
827
  function loadSettings() { try { return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem('myhi_chat_settings')) }; } catch { return { ...DEFAULT_SETTINGS }; } }
@@ -837,6 +850,24 @@
837
850
  }
838
851
  document.querySelectorAll('#seg-theme .seg').forEach(b => b.addEventListener('click', () => applyTheme(b.dataset.theme)));
839
852
  document.querySelectorAll('#seg-font .seg').forEach(b => b.addEventListener('click', () => applyFont(b.dataset.font)));
853
+ // 回车键行为(自动/发送/换行)——存 settings.enterMode,keydown 处理器读取
854
+ function updateEnterHint() {
855
+ const el = document.getElementById('enter-hint');
856
+ if (!el) return;
857
+ if (window.matchMedia('(pointer: coarse)').matches) { el.style.display = 'none'; return; } // 手机不显示
858
+ const mode = settings.enterMode || 'auto';
859
+ el.textContent = mode === 'newline' ? 'Enter 换行 · Ctrl+Enter 发送' : 'Enter 发送 · Shift+Enter 换行';
860
+ el.style.display = '';
861
+ }
862
+ function syncEnterModeUI() {
863
+ const m = settings.enterMode || 'auto';
864
+ document.querySelectorAll('#seg-enter .seg').forEach(b => b.classList.toggle('active', b.dataset.enter === m));
865
+ updateEnterHint();
866
+ }
867
+ document.querySelectorAll('#seg-enter .seg').forEach(b => b.addEventListener('click', () => {
868
+ settings.enterMode = b.dataset.enter; saveSettings(); syncEnterModeUI();
869
+ }));
870
+ updateEnterHint();
840
871
 
841
872
  // 输出累积
842
873
  let outputBuffer = '';
@@ -2035,8 +2066,17 @@
2035
2066
  }
2036
2067
  return;
2037
2068
  }
2038
- if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendCommand(); }
2039
- else if (e.key === 'ArrowUp' && !cmdInput.value) { if (historyIdx < cmdHistory.length - 1) { historyIdx++; cmdInput.value = cmdHistory[historyIdx]; } e.preventDefault(); }
2069
+ if (e.key === 'Enter') {
2070
+ if (e.isComposing || e.keyCode === 229) return; // 中文/日文输入法选词中,绝不发送
2071
+ if (e.ctrlKey || e.metaKey) { e.preventDefault(); sendCommand(); return; } // Ctrl/⌘+回车 始终发送
2072
+ const mode = settings.enterMode || 'auto';
2073
+ const sendOnEnter = mode === 'send' ? !e.shiftKey
2074
+ : mode === 'newline' ? false
2075
+ : (!e.shiftKey && !window.matchMedia('(pointer: coarse)').matches); // 自动:桌面(细指针)发送、触屏换行
2076
+ if (sendOnEnter) { e.preventDefault(); sendCommand(); }
2077
+ return; // 不发送时交给 textarea 默认插入换行
2078
+ }
2079
+ if (e.key === 'ArrowUp' && !cmdInput.value) { if (historyIdx < cmdHistory.length - 1) { historyIdx++; cmdInput.value = cmdHistory[historyIdx]; } e.preventDefault(); }
2040
2080
  else if (e.key === 'ArrowDown' && !cmdInput.value) { if (historyIdx > 0) { historyIdx--; cmdInput.value = cmdHistory[historyIdx]; } else if (historyIdx === 0) { historyIdx = -1; cmdInput.value = ''; } e.preventDefault(); }
2041
2081
  });
2042
2082
 
@@ -2405,6 +2445,7 @@
2405
2445
  document.getElementById('set-parse').checked = settings.parseOutput;
2406
2446
  document.getElementById('set-thinking').checked = settings.showThinking;
2407
2447
  document.getElementById('set-collapse').checked = settings.collapseTools;
2448
+ syncEnterModeUI();
2408
2449
  // 从服务端加载 ASR URL
2409
2450
  fetch('/api/server-config').then(r => r.json()).then(d => {
2410
2451
  document.getElementById('set-asr-url').value = d.asrUrl || '';