myagent-ai 1.7.3 → 1.7.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.7.3",
3
+ "version": "1.7.4",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
@@ -588,6 +588,13 @@ input,textarea,select{font:inherit}
588
588
  }
589
589
  .rp-section-toggle:hover{background:var(--bg4);color:var(--text)}
590
590
  .rp-section-toggle.expanded{transform:rotate(90deg)}
591
+ .rp-section-action-btn{
592
+ width:20px;height:20px;border-radius:3px;border:none;
593
+ background:transparent;color:var(--text3);
594
+ display:inline-grid;place-items:center;flex-shrink:0;
595
+ cursor:pointer;transition:var(--transition);
596
+ }
597
+ .rp-section-action-btn:hover{background:var(--bg4);color:var(--accent)}
591
598
 
592
599
  /* Master Agent Card (全权Agent) */
593
600
  .rp-master-card{
@@ -1204,8 +1211,9 @@ input,textarea,select{font:inherit}
1204
1211
  opacity:0;position:absolute;right:8px;top:50%;transform:translateY(-50%);
1205
1212
  width:24px;height:24px;border-radius:4px;display:grid;place-items:center;
1206
1213
  background:var(--bg2);transition:var(--transition);font-size:12px;color:var(--text3);
1214
+ pointer-events:none;
1207
1215
  }
1208
- .group-item:hover .group-delete{opacity:1}
1216
+ .group-item:hover .group-delete{opacity:1;pointer-events:auto}
1209
1217
  .group-delete:hover{background:var(--danger);color:#fff}
1210
1218
 
1211
1219
  /* Group Chat Messages */
@@ -1643,3 +1651,143 @@ input,textarea,select{font:inherit}
1643
1651
  font-family: inherit;
1644
1652
  white-space: pre-wrap;
1645
1653
  }
1654
+
1655
+ /* ══════════════════════════════════════════════════════
1656
+ ── Mobile Responsive (≤768px) ──
1657
+ ══════════════════════════════════════════════════════ */
1658
+
1659
+ /* ── Mobile Overlay Backdrop (placed outside media query so JS can always use it) ── */
1660
+ .mobile-overlay{
1661
+ position:fixed;inset:0;
1662
+ background:rgba(0,0,0,.4);
1663
+ z-index:45;
1664
+ opacity:0;visibility:hidden;
1665
+ transition:opacity .3s ease,visibility .3s ease;
1666
+ pointer-events:none;
1667
+ }
1668
+ .mobile-overlay.active{
1669
+ opacity:1;visibility:visible;
1670
+ pointer-events:auto;
1671
+ }
1672
+
1673
+ @media(max-width:768px){
1674
+
1675
+ /* ── Sidebar: Fixed overlay sliding from left ── */
1676
+ .sidebar{
1677
+ position:fixed;
1678
+ left:0;top:0;
1679
+ width:85vw;max-width:320px;
1680
+ height:100vh;
1681
+ z-index:50;
1682
+ transform:translateX(-100%);
1683
+ transition:transform .3s cubic-bezier(.4,0,.2,1);
1684
+ box-shadow:none;
1685
+ }
1686
+ .sidebar.mobile-open{
1687
+ transform:translateX(0);
1688
+ box-shadow:4px 0 24px rgba(0,0,0,.15);
1689
+ }
1690
+
1691
+ /* ── Agent Panel: Fixed overlay sliding from right ── */
1692
+ .agent-panel{
1693
+ position:fixed;
1694
+ right:0;top:0;
1695
+ width:85vw;max-width:340px;
1696
+ height:100vh;
1697
+ z-index:50;
1698
+ transform:translateX(100%);
1699
+ transition:transform .3s cubic-bezier(.4,0,.2,1);
1700
+ box-shadow:none;
1701
+ }
1702
+ .agent-panel.mobile-open{
1703
+ transform:translateX(0);
1704
+ box-shadow:-4px 0 24px rgba(0,0,0,.15);
1705
+ }
1706
+
1707
+ /* ── Hide desktop toggle buttons on mobile ── */
1708
+ .sidebar-toggle,
1709
+ .agent-toggle{
1710
+ display:none!important;
1711
+ }
1712
+
1713
+ /* ── Header ── */
1714
+ .main-header{
1715
+ height:50px;min-height:50px;
1716
+ padding:0 12px;
1717
+ }
1718
+ .main-title{
1719
+ font-size:14px;
1720
+ white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
1721
+ min-width:0;
1722
+ }
1723
+
1724
+ /* ── Messages ── */
1725
+ .messages-container{
1726
+ padding:12px;
1727
+ }
1728
+ .messages-inner{
1729
+ max-width:100%;
1730
+ }
1731
+ .message-bubble{
1732
+ max-width:88%;
1733
+ }
1734
+ .message-bubble pre{
1735
+ font-size:12px;
1736
+ }
1737
+
1738
+ /* ── Input Area ── */
1739
+ .input-area{
1740
+ padding:10px 12px;
1741
+ }
1742
+ .input-wrapper{
1743
+ max-width:100%;
1744
+ }
1745
+ .input-box textarea{
1746
+ min-height:60px;
1747
+ }
1748
+
1749
+ /* ── Modals ── */
1750
+ .modal,
1751
+ .agent-modal,
1752
+ .platform-modal{
1753
+ width:95%;max-height:90vh;padding:20px;
1754
+ }
1755
+
1756
+ /* ── Toast ── */
1757
+ .toast-container{
1758
+ top:12px;right:12px;left:12px;
1759
+ }
1760
+ .toast{
1761
+ max-width:100%;
1762
+ }
1763
+
1764
+ /* ── Welcome Card Capabilities ── */
1765
+ .capabilities{
1766
+ grid-template-columns:1fr 1fr;
1767
+ }
1768
+
1769
+ /* ── Execution Timer ── */
1770
+ .exec-timer{
1771
+ max-width:100%;
1772
+ }
1773
+
1774
+ /* ── Empty State ── */
1775
+ .empty-icon{
1776
+ font-size:40px;
1777
+ }
1778
+ .empty-title{
1779
+ font-size:16px;
1780
+ }
1781
+ }
1782
+
1783
+ /* ══════════════════════════════════════════════════════
1784
+ ── Very Small Phones (≤480px) ──
1785
+ ══════════════════════════════════════════════════════ */
1786
+ @media(max-width:480px){
1787
+ .message-bubble{
1788
+ max-width:92%;
1789
+ }
1790
+ .main-header{
1791
+ padding:0 8px;
1792
+ }
1793
+ }
@@ -18,6 +18,9 @@
18
18
  <div id="rightAgentsContainer"></div>
19
19
  </div>
20
20
 
21
+ <!-- Mobile Overlay -->
22
+ <div class="mobile-overlay" id="chatMobileOverlay"></div>
23
+
21
24
  <!-- Toast Container -->
22
25
  <div class="toast-container" id="toastContainer"></div>
23
26
 
@@ -28,18 +28,119 @@ initTheme();
28
28
  document.getElementById('themeToggle')?.addEventListener('click', toggleTheme);
29
29
 
30
30
  // ── Sidebar Collapse ──
31
+ function isMobile() { return window.innerWidth <= 768; }
32
+
31
33
  function toggleSidebar() {
32
34
  const sidebar = document.getElementById('sidebar');
33
35
  const toggleBtn = document.getElementById('sidebarToggle');
34
36
  if (!sidebar) return;
35
- sidebar.classList.toggle('collapsed');
36
- const isCollapsed = sidebar.classList.contains('collapsed');
37
- if (toggleBtn) toggleBtn.textContent = isCollapsed ? '' : '◀';
38
- localStorage.setItem('myagent-sidebar-collapsed', isCollapsed);
37
+ if (isMobile()) {
38
+ // Mobile: toggle as overlay
39
+ const overlay = document.getElementById('chatMobileOverlay');
40
+ const isOpen = sidebar.classList.contains('mobile-open');
41
+ if (isOpen) {
42
+ closeMobileSidebar();
43
+ } else {
44
+ sidebar.classList.add('mobile-open');
45
+ if (overlay) overlay.classList.add('active');
46
+ // Close agent panel if open
47
+ closeMobileAgentPanel();
48
+ }
49
+ } else {
50
+ // Desktop: collapse/expand
51
+ sidebar.classList.toggle('collapsed');
52
+ const isCollapsed = sidebar.classList.contains('collapsed');
53
+ if (toggleBtn) toggleBtn.textContent = isCollapsed ? '▶' : '◀';
54
+ localStorage.setItem('myagent-sidebar-collapsed', isCollapsed);
55
+ }
56
+ }
57
+
58
+ function closeMobileSidebar() {
59
+ const sidebar = document.getElementById('sidebar');
60
+ const overlay = document.getElementById('chatMobileOverlay');
61
+ if (sidebar) sidebar.classList.remove('mobile-open');
62
+ if (overlay) overlay.classList.remove('active');
63
+ }
64
+
65
+ function toggleMobileAgentPanel() {
66
+ const panel = document.getElementById('agentPanel');
67
+ const overlay = document.getElementById('chatMobileOverlay');
68
+ if (!panel) return;
69
+ const isOpen = panel.classList.contains('mobile-open');
70
+ if (isOpen) {
71
+ closeMobileAgentPanel();
72
+ } else {
73
+ panel.classList.add('mobile-open');
74
+ if (overlay) overlay.classList.add('active');
75
+ // Close sidebar if open
76
+ closeMobileSidebar();
77
+ }
78
+ }
79
+
80
+ function closeMobileAgentPanel() {
81
+ const panel = document.getElementById('agentPanel');
82
+ const overlay = document.getElementById('chatMobileOverlay');
83
+ if (panel) panel.classList.remove('mobile-open');
84
+ if (overlay) overlay.classList.remove('active');
85
+ }
86
+
87
+ // Mobile overlay click closes both panels
88
+ document.addEventListener('click', function(e) {
89
+ if (e.target && e.target.id === 'chatMobileOverlay') {
90
+ closeMobileSidebar();
91
+ closeMobileAgentPanel();
92
+ }
93
+ });
94
+
95
+ // Show/hide mobile agents button based on screen size
96
+ function checkChatMobile() {
97
+ const btn = document.getElementById('mobileAgentsBtn');
98
+ if (btn) btn.style.display = isMobile() ? 'grid' : 'none';
39
99
  }
40
- // Restore sidebar state
100
+ window.addEventListener('resize', checkChatMobile);
101
+ // Run after DOM ready
102
+ if (document.readyState === 'loading') {
103
+ document.addEventListener('DOMContentLoaded', checkChatMobile);
104
+ } else {
105
+ checkChatMobile();
106
+ }
107
+
108
+ // Override toggleAgentPanel for mobile (deferred since function is defined later)
109
+ var _origToggleAgentPanel = null;
110
+ (function() {
111
+ var origDef = toggleAgentPanel;
112
+ if (typeof origDef === 'function') {
113
+ _origToggleAgentPanel = origDef;
114
+ toggleAgentPanel = function() {
115
+ if (isMobile()) {
116
+ toggleMobileAgentPanel();
117
+ } else {
118
+ _origToggleAgentPanel();
119
+ }
120
+ };
121
+ }
122
+ })();
123
+ // Also override after definition via setTimeout (fallback)
124
+ setTimeout(function() {
125
+ if (!_origToggleAgentPanel && typeof toggleAgentPanel === 'function') {
126
+ // Check if it's already the mobile version
127
+ var testFn = toggleAgentPanel.toString();
128
+ if (testFn.indexOf('toggleMobileAgentPanel') === -1) {
129
+ _origToggleAgentPanel = toggleAgentPanel;
130
+ toggleAgentPanel = function() {
131
+ if (isMobile()) {
132
+ toggleMobileAgentPanel();
133
+ } else {
134
+ _origToggleAgentPanel();
135
+ }
136
+ };
137
+ }
138
+ }
139
+ }, 0);
140
+
141
+ // Restore sidebar state (desktop only)
41
142
  (function() {
42
- if (localStorage.getItem('myagent-sidebar-collapsed') === 'true') {
143
+ if (localStorage.getItem('myagent-sidebar-collapsed') === 'true' && !isMobile()) {
43
144
  const sidebar = document.getElementById('sidebar');
44
145
  const toggleBtn = document.getElementById('sidebarToggle');
45
146
  if (sidebar) sidebar.classList.add('collapsed');
@@ -966,6 +1067,7 @@ async function selectAgent(agentPath) {
966
1067
  // 如果 loadSessions 已经 auto-selected 了 session,UI 已由 selectSession 设置好,不再覆盖
967
1068
 
968
1069
  document.getElementById('userInput').focus();
1070
+ if (isMobile()) closeMobileAgentPanel();
969
1071
  // Reload task plan if in exec mode
970
1072
  if (state.chatMode === 'exec') loadTaskPlan();
971
1073
  // Reset escalation and update exec mode UI
@@ -1434,9 +1536,13 @@ function newChat() {
1434
1536
  async function selectSession(id) {
1435
1537
  if (id === '__new__') {
1436
1538
  newChat();
1539
+ if (isMobile()) closeMobileSidebar();
1540
+ return;
1541
+ }
1542
+ if (state.activeSessionId === id && state.messages.length > 0) {
1543
+ if (isMobile()) closeMobileSidebar();
1437
1544
  return;
1438
1545
  }
1439
- if (state.activeSessionId === id && state.messages.length > 0) return;
1440
1546
 
1441
1547
  // 重置生成状态,防止残留的 isGenerating=true 导致输入框锁定
1442
1548
  state.isGenerating = false;
@@ -1470,6 +1576,7 @@ async function selectSession(id) {
1470
1576
  }
1471
1577
  renderMessages();
1472
1578
  document.getElementById('userInput').focus();
1579
+ if (isMobile()) closeMobileSidebar();
1473
1580
  }
1474
1581
 
1475
1582
  async function deleteSession(id) {
@@ -37,7 +37,7 @@ async function fetchGroups() {
37
37
  async function createGroupApi(name, description, emoji, color, members) {
38
38
  return await api('/api/groups', {
39
39
  method: 'POST',
40
- body: JSON.stringify({ name, description, emoji, color, members }),
40
+ body: JSON.stringify({ name, description, avatar_emoji: emoji, avatar_color: color, members }),
41
41
  });
42
42
  }
43
43
 
@@ -109,13 +109,12 @@ function renderGroups() {
109
109
  var memberCount = (g.members || []).length;
110
110
  var isActive = g.id === currentGroupId;
111
111
  html += '<div class="group-item ' + (isActive ? 'active' : '') + '" onclick="selectGroup(\'' + escapeHtml(g.id) + '\')" title="' + escapeHtml(g.name) + '">';
112
- html += '<div class="group-icon">' + escapeHtml(g.emoji || '👥') + '</div>';
112
+ html += '<div class="group-icon"' + (g.avatar_color ? ' style="background:' + escapeHtml(g.avatar_color) + ';color:#fff"' : '') + '>' + escapeHtml(g.avatar_emoji || g.emoji || '👥') + '</div>';
113
113
  html += '<div class="group-info">';
114
114
  html += '<div class="group-name">' + escapeHtml(g.name) + '</div>';
115
115
  html += '<div class="group-preview">' + escapeHtml(g.description || memberCount + ' 位成员') + '</div>';
116
116
  html += '</div>';
117
117
  html += '<span class="group-badge">' + memberCount + '</span>';
118
- html += '<button class="group-delete" onclick="event.stopPropagation();deleteGroupConfirm(\'' + escapeHtml(g.id) + '\')" title="删除">✕</button>';
119
118
  html += '</div>';
120
119
  }
121
120
  listEl.innerHTML = html;
@@ -145,7 +144,7 @@ async function selectGroup(gid) {
145
144
  var groupData = await getGroup(gid);
146
145
 
147
146
  // Update header
148
- document.getElementById('headerTitle').textContent = (groupData.emoji || '👥') + ' ' + groupData.name;
147
+ document.getElementById('headerTitle').textContent = (groupData.avatar_emoji || groupData.emoji || '👥') + ' ' + groupData.name;
149
148
  document.getElementById('activeAgentBadge').style.display = 'none';
150
149
  document.getElementById('groupBackBtn').style.display = '';
151
150
  document.getElementById('groupSettingsBtn').style.display = '';
@@ -217,7 +216,7 @@ function renderGroupMessages() {
217
216
  if (groupMessages.length === 0) {
218
217
  var group = groups.find(function(g) { return g.id === currentGroupId; });
219
218
  html = '<div class="welcome-card">'
220
- + '<h2><span class="emoji">' + escapeHtml((group && group.emoji) || '👥') + '</span>'
219
+ + '<h2><span class="emoji">' + escapeHtml((group && (group.avatar_emoji || group.emoji)) || '👥') + '</span>'
221
220
  + ' <span>群聊: ' + escapeHtml((group && group.name) || '') + '</span></h2>'
222
221
  + '<p class="subtitle">向所有成员发送消息,每个 Agent 会分别回复</p>'
223
222
  + '</div>';
@@ -426,8 +425,8 @@ async function showGroupSettingsModal() {
426
425
  + '</div>'
427
426
  + '<div class="agent-form-group"><label>头像</label>'
428
427
  + '<div class="agent-avatar-picker" id="groupSettingsEmojiPicker">'
429
- + emojis.map(function(e) { return '<div class="agent-avatar-option ' + ((groupData.emoji || '👥') === e ? 'selected' : '') + '" onclick="pickGroupSettingsEmoji(this,\'' + e + '\')" data-emoji="' + e + '">' + e + '</div>'; }).join('')
430
- + '</div><input type="hidden" id="groupSettingsEmoji" value="' + escapeHtml(groupData.emoji || '👥') + '"></div>'
428
+ + emojis.map(function(e) { return '<div class="agent-avatar-option ' + ((groupData.avatar_emoji || groupData.emoji || '👥') === e ? 'selected' : '') + '" onclick="pickGroupSettingsEmoji(this,\'' + e + '\')" data-emoji="' + e + '">' + e + '</div>'; }).join('')
429
+ + '</div><input type="hidden" id="groupSettingsEmoji" value="' + escapeHtml(groupData.avatar_emoji || groupData.emoji || '👥') + '"></div>'
431
430
  + '<div class="agent-form-group"><label>成员 (' + members.length + ')</label>'
432
431
  + '<div style="margin-bottom:8px">'
433
432
  + '<button class="config-action-btn" onclick="showAddMemberToGroup()" style="font-size:12px;padding:6px 12px">'
@@ -463,14 +462,14 @@ async function saveGroupSettings() {
463
462
  await updateGroup(currentGroupId, {
464
463
  name: name,
465
464
  description: document.getElementById('groupSettingsDesc').value.trim(),
466
- emoji: document.getElementById('groupSettingsEmoji').value
465
+ avatar_emoji: document.getElementById('groupSettingsEmoji').value
467
466
  });
468
467
  toast('群聊设置已保存', 'success');
469
468
  closeGroupModal();
470
469
  await fetchGroups();
471
470
  var group = groups.find(function(g) { return g.id === currentGroupId; });
472
471
  if (group) {
473
- document.getElementById('headerTitle').textContent = (group.emoji || '👥') + ' ' + group.name;
472
+ document.getElementById('headerTitle').textContent = (group.avatar_emoji || group.emoji || '👥') + ' ' + group.name;
474
473
  }
475
474
  } catch (e) {
476
475
  toast('保存失败: ' + e.message, 'error');
@@ -12,6 +12,9 @@
12
12
  </span>
13
13
  </div>
14
14
  <div class="header-actions">
15
+ <button class="header-btn" id="mobileAgentsBtn" onclick="toggleMobileAgentPanel()" title="Agents" style="display:none">
16
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
17
+ </button>
15
18
  <button class="header-btn" id="groupBackBtn" onclick="exitGroupChat()" title="返回对话" style="display:none">
16
19
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>
17
20
  </button>
@@ -39,7 +39,12 @@
39
39
  <div id="rpGroupSection">
40
40
  <div class="rp-section-header" onclick="toggleRpSection('group')">
41
41
  <span class="rp-section-title">👥 群聊</span>
42
- <span class="rp-section-toggle expanded" id="rpGroupToggle">▶</span>
42
+ <div style="display:flex;align-items:center;gap:4px">
43
+ <button class="rp-section-action-btn" onclick="event.stopPropagation();showCreateGroupModal()" title="创建群聊">
44
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
45
+ </button>
46
+ <span class="rp-section-toggle expanded" id="rpGroupToggle">▶</span>
47
+ </div>
43
48
  </div>
44
49
  <div class="rp-section-body" id="rpGroupBody">
45
50
  <div class="group-list" id="groupList">
package/web/ui/index.html CHANGED
@@ -151,6 +151,29 @@ tr:hover{background:var(--surface2)}
151
151
  [data-theme="claude"] .badge-red{background:#c9444422}
152
152
  [data-theme="claude"] .badge-yellow{background:#c4862b22}
153
153
  [data-theme="claude"] .badge-blue{background:#4a7fc922}
154
+ /* ── Mobile Responsive ── */
155
+ @media(max-width:768px){
156
+ body{flex-direction:column}
157
+ .sidebar{position:fixed;left:0;top:0;height:100vh;width:260px;max-width:80vw;z-index:50;transform:translateX(-100%);transition:transform .3s ease;flex-shrink:0}
158
+ .sidebar.mobile-open{transform:translateX(0)}
159
+ .sidebar.collapsed{width:260px;transform:translateX(-100%)}
160
+ .sidebar.collapsed.mobile-open{transform:translateX(0)}
161
+ .sidebar-toggle{display:none!important}
162
+ .mobile-overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:45;display:none}
163
+ .mobile-overlay.active{display:block}
164
+ .header{padding:12px 16px}
165
+ .header h2{font-size:16px}
166
+ .content{padding:16px}
167
+ .grid{grid-template-columns:1fr}
168
+ .form-row{grid-template-columns:1fr}
169
+ .table-wrap{overflow-x:auto}
170
+ .modal{width:95%;max-height:90vh;padding:16px}
171
+ .modal-wide{max-width:95%}
172
+ .tabs{gap:0;overflow-x:auto}
173
+ .toast{left:16px;right:16px;bottom:16px}
174
+ .agent-card{flex-direction:column;align-items:flex-start}
175
+ .agent-card .flex.flex-col{flex-direction:row;gap:4px}
176
+ }
154
177
  </style>
155
178
  </head>
156
179
  <body>
@@ -181,8 +204,9 @@ tr:hover{background:var(--surface2)}
181
204
  · <a href="#" onclick="checkUpdate(true)" style="color:var(--primary)">检查更新</a>
182
205
  </div>
183
206
  </div>
207
+ <div class="mobile-overlay" id="adminMobileOverlay" onclick="closeMobileSidebar()"></div>
184
208
  <div class="main">
185
- <div class="header"><div style="display:flex;align-items:center;gap:12px"><h2 id="pageTitle">📊 仪表盘</h2><button class="header-btn" id="themeToggle" title="切换主题"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div><div style="display:flex;align-items:center;gap:8px"><span class="status-dot"></span>运行中</div></div>
209
+ <div class="header"><div style="display:flex;align-items:center;gap:12px"><button class="header-btn" id="mobileMenuBtn" onclick="toggleMobileSidebar()" style="display:none" title="菜单"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg></button><h2 id="pageTitle">📊 仪表盘</h2><button class="header-btn" id="themeToggle" title="切换主题"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div><div style="display:flex;align-items:center;gap:8px"><span class="status-dot"></span>运行中</div></div>
186
210
  <div class="content" id="content"></div>
187
211
  </div>
188
212
  <div id="modalContainer"></div>
@@ -250,12 +274,28 @@ function initTheme(){const s=localStorage.getItem('myagent-theme')||'claude';doc
250
274
  function toggleTheme(){const c=document.documentElement.getAttribute('data-theme')||'claude';const n=c==='claude'?'dark':'claude';document.documentElement.setAttribute('data-theme',n);localStorage.setItem('myagent-theme',n);updateThemeIcon(n)}
251
275
  function updateThemeIcon(t){const b=document.getElementById('themeToggle');if(!b)return;if(t==='dark'){b.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';b.title='切换到 Claude 风格'}else{b.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';b.title='切换到夜间模式'}}
252
276
  // ── Sidebar Collapse ──
253
- function toggleSidebar(){const s=document.getElementById('adminSidebar');const t=document.getElementById('sidebarToggle');if(!s)return;s.classList.toggle('collapsed');const isCollapsed=s.classList.contains('collapsed');t.textContent=isCollapsed?'▶':'◀';localStorage.setItem('myagent-admin-sidebar-collapsed',isCollapsed)}
277
+ function toggleSidebar(){const s=document.getElementById('adminSidebar');const t=document.getElementById('sidebarToggle');if(!s)return;s.classList.toggle('collapsed');const isCollapsed=s.classList.contains('collapsed');t.textContent=isCollapsed?'▶':'◀';localStorage.setItem('myagent-admin-sidebar-collapsed',isCollapsed);closeMobileSidebar()}
254
278
  // Initialize
255
279
  initTheme();
256
280
  document.getElementById('themeToggle')?.addEventListener('click',toggleTheme);
257
281
  if(localStorage.getItem('myagent-admin-sidebar-collapsed')==='true'){document.getElementById('adminSidebar')?.classList.add('collapsed');const t=document.getElementById('sidebarToggle');if(t)t.textContent='▶'}
258
282
 
283
+ // ── Mobile Sidebar ──
284
+ function toggleMobileSidebar(){
285
+ const s=document.getElementById('adminSidebar');
286
+ const o=document.getElementById('adminMobileOverlay');
287
+ s.classList.toggle('mobile-open');
288
+ o.classList.toggle('active');
289
+ }
290
+ function closeMobileSidebar(){
291
+ document.getElementById('adminSidebar').classList.remove('mobile-open');
292
+ document.getElementById('adminMobileOverlay').classList.remove('active');
293
+ }
294
+ // Show hamburger on mobile
295
+ function checkMobile(){const btn=document.getElementById('mobileMenuBtn');if(btn)btn.style.display=window.innerWidth<=768?'grid':'none'}
296
+ window.addEventListener('resize',checkMobile);
297
+ checkMobile();
298
+
259
299
  loadVersion();
260
300
  setTimeout(()=>checkUpdate(false),30000);
261
301
  function showConfirm(title,msg,onOk){
@@ -266,6 +306,7 @@ function showConfirm(title,msg,onOk){
266
306
  }
267
307
 
268
308
  function showPage(page){
309
+ closeMobileSidebar();
269
310
  currentPage=page;
270
311
  document.querySelectorAll('.nav-item').forEach((n,i)=>n.classList.toggle('active',Object.keys(pages)[i]===page));
271
312
  $('pageTitle').textContent=pages[page]||page;