@wendongfly/myhi 1.3.32 → 1.3.34

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/dist/chat.html CHANGED
@@ -62,6 +62,14 @@
62
62
  .msg-assistant .content pre code { background: none; padding: 0; }
63
63
  .msg-assistant .content ul, .msg-assistant .content ol { padding-left: 1.5rem; margin: 0.3rem 0; }
64
64
  .msg-assistant .content strong { color: #fff; }
65
+ .msg-assistant .content a { color: #58a6ff; text-decoration: none; word-break: break-all; }
66
+ .msg-assistant .content a:hover { text-decoration: underline; }
67
+ .md-table { border-collapse: collapse; margin: 0.5rem 0; font-size: 0.82rem; width: 100%; display: block; overflow-x: auto; white-space: nowrap; }
68
+ .md-table thead { background: #161b22; }
69
+ .md-table th, .md-table td { border: 1px solid #30363d; padding: 0.4rem 0.7rem; text-align: left; }
70
+ .md-table th { color: #fff; font-weight: 600; }
71
+ .md-table tbody tr:nth-child(even) { background: #0d1117; }
72
+ .md-table tbody tr:hover { background: #1c2128; }
65
73
 
66
74
  /* 工具调用组 */
67
75
  .msg-tool-group { border-left: 3px solid #58a6ff; margin-left: 0.3rem; padding-left: 0.6rem; margin-bottom: 0.3rem; }
@@ -678,7 +686,21 @@
678
686
  let html = '';
679
687
  let inCode = false, codeLang = '', codeLines = [];
680
688
 
681
- for (const line of lines) {
689
+ // 行内格式化辅助函数
690
+ const fmtInline = (s) => s
691
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
692
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
693
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
694
+ .replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
695
+
696
+ // 表格检测:| ... | 后跟 |---|---| 分隔行
697
+ const isTableRow = (s) => /^\s*\|(.+)\|\s*$/.test(s);
698
+ const isTableSep = (s) => /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)+\|?\s*$/.test(s);
699
+ const splitCells = (s) => s.trim().replace(/^\||\|$/g, '').split('|').map(c => c.trim());
700
+
701
+ for (let i = 0; i < lines.length; i++) {
702
+ const line = lines[i];
703
+
682
704
  if (!inCode && /^```(\w*)/.test(line)) {
683
705
  inCode = true; codeLang = line.match(/^```(\w*)/)[1]; codeLines = [];
684
706
  continue;
@@ -689,15 +711,31 @@
689
711
  }
690
712
  if (inCode) { codeLines.push(line); continue; }
691
713
 
714
+ // 表格
715
+ if (isTableRow(line) && i + 1 < lines.length && isTableSep(lines[i + 1])) {
716
+ const header = splitCells(line);
717
+ let tableHtml = '<table class="md-table"><thead><tr>' +
718
+ header.map(c => `<th>${fmtInline(escHtml(c))}</th>`).join('') +
719
+ '</tr></thead><tbody>';
720
+ i += 2; // 跳过表头和分隔行
721
+ while (i < lines.length && isTableRow(lines[i])) {
722
+ const cells = splitCells(lines[i]);
723
+ tableHtml += '<tr>' + cells.map(c => `<td>${fmtInline(escHtml(c))}</td>`).join('') + '</tr>';
724
+ i++;
725
+ }
726
+ tableHtml += '</tbody></table>';
727
+ html += tableHtml;
728
+ i--; // 回退一步让外层 for 循环正确递增
729
+ continue;
730
+ }
731
+
692
732
  let l = escHtml(line);
693
733
  // 标题
694
- if (/^### /.test(l)) { html += `<h4>${l.slice(4)}</h4>`; continue; }
695
- if (/^## /.test(l)) { html += `<h3>${l.slice(3)}</h3>`; continue; }
696
- if (/^# /.test(l)) { html += `<h2>${l.slice(2)}</h2>`; continue; }
734
+ if (/^### /.test(l)) { html += `<h4>${fmtInline(l.slice(4))}</h4>`; continue; }
735
+ if (/^## /.test(l)) { html += `<h3>${fmtInline(l.slice(3))}</h3>`; continue; }
736
+ if (/^# /.test(l)) { html += `<h2>${fmtInline(l.slice(2))}</h2>`; continue; }
697
737
  // 行内格式
698
- l = l.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
699
- l = l.replace(/\*(.+?)\*/g, '<em>$1</em>');
700
- l = l.replace(/`([^`]+)`/g, '<code>$1</code>');
738
+ l = fmtInline(l);
701
739
  // 列表
702
740
  if (/^- /.test(l)) { html += `<li>${l.slice(2)}</li>`; continue; }
703
741
  if (/^\d+\. /.test(l)) { html += `<li>${l.replace(/^\d+\.\s*/, '')}</li>`; continue; }
package/dist/index.html CHANGED
@@ -419,6 +419,7 @@
419
419
  <div style="display:flex;align-items:center;gap:0.6rem">
420
420
  <span id="user-name" style="font-size:0.8rem;color:var(--muted)"></span>
421
421
  <button id="usage-btn" onclick="showUsage()" style="background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">用量</button>
422
+ <button id="settings-btn" onclick="openGatewaySheet()" style="display:none;background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">设置</button>
422
423
  <button id="logout-btn" onclick="doLogout()" style="display:none;background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">退出</button>
423
424
  <div id="conn-dot"></div>
424
425
  </div>
@@ -437,6 +438,32 @@
437
438
  </div>
438
439
  </div>
439
440
 
441
+ <!-- Gateway settings sheet -->
442
+ <div id="gw-backdrop" onclick="closeGatewaySheet()" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.55);z-index:40"></div>
443
+ <style>#gw-backdrop.open { display:block; } #gw-sheet { position:fixed; bottom:-100%; left:0; right:0; background:var(--surface); border-top-left-radius:18px; border-top-right-radius:18px; padding:1rem 1.1rem 1.4rem; z-index:41; transition:bottom 0.25s ease; max-width:500px; margin:0 auto; } #gw-sheet.open { bottom:0; }</style>
444
+ <div id="gw-sheet">
445
+ <div class="sheet-handle"></div>
446
+ <div class="sheet-title">大模型网关配置</div>
447
+ <div style="font-size:0.78rem;color:var(--muted);margin-bottom:0.8rem;line-height:1.5">
448
+ 配置后,新建的 Claude 会话将自动使用此网关。<br>
449
+ 对应环境变量:<code style="background:var(--bg);padding:0.1em 0.3em;border-radius:3px">ANTHROPIC_BASE_URL</code> / <code style="background:var(--bg);padding:0.1em 0.3em;border-radius:3px">ANTHROPIC_API_KEY</code>
450
+ </div>
451
+ <div class="field">
452
+ <label>网关 URL</label>
453
+ <input id="gw-url" type="text" placeholder="https://your-gateway.example.com" autocomplete="off" spellcheck="false">
454
+ </div>
455
+ <div class="field">
456
+ <label>Token <span style="font-weight:400;text-transform:none">(API Key)</span></label>
457
+ <div style="display:flex;gap:0.5rem">
458
+ <input id="gw-key" type="password" placeholder="sk-ant-... 或留空保持不变" autocomplete="off" spellcheck="false" style="flex:1">
459
+ <button onclick="clearGatewayKey()" type="button" style="flex-shrink:0;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:0 0.75rem;color:var(--muted);font-size:0.75rem;cursor:pointer">清空</button>
460
+ </div>
461
+ </div>
462
+ <button onclick="saveGateway()" style="width:100%;margin-top:0.5rem">保存</button>
463
+ <button onclick="closeGatewaySheet()" style="width:100%;background:var(--bg);color:var(--muted);border:1px solid var(--border);border-radius:10px;padding:0.7rem;font-size:0.85rem;cursor:pointer;margin-top:0.5rem">取消</button>
464
+ <div id="gw-msg" style="text-align:center;font-size:0.78rem;color:var(--muted);margin-top:0.6rem;min-height:1.2em"></div>
465
+ </div>
466
+
440
467
  <!-- FAB -->
441
468
  <button id="fab" onclick="openSheet()">+</button>
442
469
 
@@ -523,6 +550,7 @@
523
550
 
524
551
  // 加载当前用户信息(独占模式显示用户名、退出按钮、锁定目录)
525
552
  let _userDir = null;
553
+ let _isAdmin = false;
526
554
  fetch('/api/me').then(r => r.json()).then(data => {
527
555
  if ((data.exclusive || data.hasUsers) && data.name) {
528
556
  document.getElementById('user-name').textContent = data.name;
@@ -535,8 +563,63 @@
535
563
  document.getElementById('drive-bar').style.display = 'none';
536
564
  renderRecentDirs();
537
565
  }
566
+ _isAdmin = data.role === 'admin';
567
+ if (_isAdmin) document.getElementById('settings-btn').style.display = '';
538
568
  }).catch(() => {});
539
569
 
570
+ // ── 大模型网关配置 ─────────────────────────────────
571
+ window.openGatewaySheet = async function() {
572
+ document.getElementById('gw-backdrop').classList.add('open');
573
+ document.getElementById('gw-sheet').classList.add('open');
574
+ document.getElementById('gw-msg').textContent = '';
575
+ try {
576
+ const r = await fetch('/api/gateway');
577
+ if (!r.ok) throw new Error('权限不足');
578
+ const d = await r.json();
579
+ document.getElementById('gw-url').value = d.baseUrl || '';
580
+ document.getElementById('gw-key').value = d.apiKey || '';
581
+ document.getElementById('gw-key').dataset.masked = d.hasKey ? '1' : '';
582
+ } catch (e) {
583
+ document.getElementById('gw-msg').textContent = '加载失败: ' + e.message;
584
+ }
585
+ };
586
+ window.closeGatewaySheet = function() {
587
+ document.getElementById('gw-backdrop').classList.remove('open');
588
+ document.getElementById('gw-sheet').classList.remove('open');
589
+ };
590
+ window.saveGateway = async function() {
591
+ const baseUrl = document.getElementById('gw-url').value.trim();
592
+ let apiKey = document.getElementById('gw-key').value.trim();
593
+ // 如果用户没改(还是掩码),后端会保留原值
594
+ const msg = document.getElementById('gw-msg');
595
+ msg.textContent = '保存中...';
596
+ try {
597
+ const r = await fetch('/api/gateway', {
598
+ method: 'POST',
599
+ headers: { 'Content-Type': 'application/json' },
600
+ body: JSON.stringify({ baseUrl, apiKey }),
601
+ });
602
+ const d = await r.json();
603
+ if (d.ok) {
604
+ msg.style.color = '#3fb950';
605
+ msg.textContent = '已保存。下次新建 Claude 会话自动生效';
606
+ setTimeout(() => closeGatewaySheet(), 1200);
607
+ } else {
608
+ msg.style.color = '#f85149';
609
+ msg.textContent = '保存失败: ' + (d.error || '未知错误');
610
+ }
611
+ } catch (e) {
612
+ msg.style.color = '#f85149';
613
+ msg.textContent = '保存失败: ' + e.message;
614
+ }
615
+ };
616
+ window.clearGatewayKey = function() {
617
+ const inp = document.getElementById('gw-key');
618
+ inp.value = '';
619
+ inp.dataset.masked = '';
620
+ inp.focus();
621
+ };
622
+
540
623
  async function showUsage() {
541
624
  try {
542
625
  const res = await fetch('/api/usage');