@wendongfly/myhi 1.3.191 → 1.3.192
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/admin.html +80 -59
- package/dist/chat.html +12 -5
- package/dist/grid.html +2 -1
- package/dist/index.html +269 -20
- package/dist/index.js +1 -1
- package/dist/index.min.js +60 -60
- package/dist/login.html +175 -163
- package/package.json +1 -1
package/dist/admin.html
CHANGED
|
@@ -5,61 +5,73 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>myhi 管理</title>
|
|
7
7
|
<script src="/lib/machine.js"></script>
|
|
8
|
+
<script src="/theme.js"></script>
|
|
9
|
+
<script>
|
|
10
|
+
// 主题与其他页面一致:浏览器选过的优先,否则服务端默认(新装浅色、老机器深色)
|
|
11
|
+
(function(){try{var t=localStorage.getItem('myhi_theme')||window.MYHI_DEFAULT_THEME||'dark';
|
|
12
|
+
if(t==='system') t=matchMedia('(prefers-color-scheme: light)').matches?'light':'dark';
|
|
13
|
+
if(t!=='dark') document.documentElement.setAttribute('data-theme',t);}catch(e){}})();
|
|
14
|
+
</script>
|
|
8
15
|
<style>
|
|
16
|
+
:root { --bg:#0d1117; --surface:#161b22; --text:#e6edf3; --text2:#b1bac4; --text3:#8b949e; --muted:#6e7681;
|
|
17
|
+
--line:rgba(255,255,255,0.06); --border:rgba(255,255,255,0.12); --border2:#30363d; --accent:#7c3aed; }
|
|
18
|
+
:root[data-theme="light"] { --bg:#f6f8fa; --surface:#ffffff; --text:#1f2328; --text2:#424a53; --text3:#57606a; --muted:#6e7781;
|
|
19
|
+
--line:rgba(0,0,0,0.06); --border:#d0d7de; --border2:#d0d7de; --accent:#8250df; }
|
|
20
|
+
:root[data-theme="black"] { --bg:#000000; --surface:#0a0a0a; --border2:#262626; }
|
|
9
21
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
10
|
-
body { font-family: -apple-system, system-ui, sans-serif; background:
|
|
22
|
+
body { font-family: -apple-system, system-ui, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; }
|
|
11
23
|
.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 1rem; }
|
|
12
|
-
.login-box { background:
|
|
24
|
+
.login-box { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; width: 100%; max-width: 360px; }
|
|
13
25
|
.login-box h2 { text-align: center; margin-bottom: 1.5rem; font-size: 1.2rem; }
|
|
14
|
-
.login-box input { width: 100%; padding: 0.8rem; background:
|
|
15
|
-
.login-box button { width: 100%; padding: 0.8rem; background:
|
|
26
|
+
.login-box input { width: 100%; padding: 0.8rem; background: var(--bg); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: 1rem; margin-bottom: 1rem; }
|
|
27
|
+
.login-box button { width: 100%; padding: 0.8rem; background: var(--accent); color: #fff; border: none; border-radius: 10px; font-size: 1rem; font-weight: 600; cursor: pointer; }
|
|
16
28
|
.login-box .err { color: #f85149; font-size: 0.85rem; text-align: center; margin-bottom: 0.5rem; min-height: 1.2em; }
|
|
17
29
|
.wrap { max-width: 640px; margin: 0 auto; padding: 1rem; }
|
|
18
|
-
.header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 0; border-bottom: 1px solid
|
|
30
|
+
.header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 0; border-bottom: 1px solid var(--line); margin-bottom: 1.5rem; }
|
|
19
31
|
.header h1 { font-size: 1.1rem; }
|
|
20
|
-
.header .logout { background: none; border: 1px solid
|
|
21
|
-
.ver-card { background:
|
|
22
|
-
.ver-big { font-size: 2rem; font-weight: 800; color:
|
|
23
|
-
.ver-sub { font-size: 0.85rem; color:
|
|
32
|
+
.header .logout { background: none; border: 1px solid var(--border); color: var(--text3); padding: 0.3rem 0.8rem; border-radius: 8px; cursor: pointer; font-size: 0.8rem; }
|
|
33
|
+
.ver-card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 1.2rem 1.5rem; margin-bottom: 1.2rem; }
|
|
34
|
+
.ver-big { font-size: 2rem; font-weight: 800; color: var(--accent); }
|
|
35
|
+
.ver-sub { font-size: 0.85rem; color: var(--text3); margin-top: 0.5rem; }
|
|
24
36
|
.ver-sub b { color: #3fb950; }
|
|
25
37
|
.actions { display: flex; gap: 0.8rem; margin-bottom: 1.5rem; }
|
|
26
38
|
.actions button { flex: 1; padding: 0.8rem; border: none; border-radius: 10px; font-size: 0.9rem; font-weight: 600; cursor: pointer; }
|
|
27
|
-
.btn-up { background:
|
|
28
|
-
.btn-up:disabled { background:
|
|
39
|
+
.btn-up { background: var(--accent); color: #fff; }
|
|
40
|
+
.btn-up:disabled { background: var(--border2); color: var(--muted); cursor: not-allowed; }
|
|
29
41
|
.btn-up.glow { background: #3fb950; animation: pulse 2s infinite; }
|
|
30
|
-
.btn-re { background:
|
|
42
|
+
.btn-re { background: var(--line); color: var(--text); border: 1px solid var(--border) !important; }
|
|
31
43
|
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.7} }
|
|
32
|
-
.card { background:
|
|
44
|
+
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 1rem 1.2rem; margin-bottom: 1rem; }
|
|
33
45
|
.card h3 { font-size: 0.9rem; font-weight: 700; margin-bottom: 0.8rem; }
|
|
34
|
-
.row { font-size: 0.82rem; color:
|
|
35
|
-
.row + .row { border-top: 1px solid
|
|
46
|
+
.row { font-size: 0.82rem; color: var(--text2); padding: 0.5rem 0; line-height: 1.5; display: flex; justify-content: space-between; align-items: center; }
|
|
47
|
+
.row + .row { border-top: 1px solid var(--line); }
|
|
36
48
|
.row-actions { display: flex; gap: 0.4rem; }
|
|
37
|
-
.row-actions button { background: none; border: 1px solid
|
|
38
|
-
.row-actions button:hover { color:
|
|
49
|
+
.row-actions button { background: none; border: 1px solid var(--border); color: var(--text3); padding: 0.2rem 0.5rem; border-radius: 6px; cursor: pointer; font-size: 0.75rem; }
|
|
50
|
+
.row-actions button:hover { color: var(--text); border-color: var(--border2); }
|
|
39
51
|
.row-actions .del { color: #f85149; border-color: rgba(248,81,73,0.3); }
|
|
40
|
-
.inp { padding: 0.5rem 0.7rem; background:
|
|
52
|
+
.inp { padding: 0.5rem 0.7rem; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: 0.85rem; width: 100%; }
|
|
41
53
|
.inp-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
|
|
42
54
|
.inp-row .inp { flex: 1; }
|
|
43
|
-
.btn-sm { padding: 0.5rem 1rem; background:
|
|
44
|
-
.btn-sm.ghost { background: transparent; color:
|
|
55
|
+
.btn-sm { padding: 0.5rem 1rem; background: var(--accent); color: #fff; border: none; border-radius: 8px; cursor: pointer; font-size: 0.85rem; white-space: nowrap; }
|
|
56
|
+
.btn-sm.ghost { background: transparent; color: var(--text2); border: 1px solid var(--border); }
|
|
45
57
|
.mode-status { font-size: 0.85rem; line-height: 1.6; padding: 0.6rem 0.75rem; border-radius: 10px; background: rgba(124,58,237,0.1); border: 1px solid rgba(124,58,237,0.25); margin-bottom: 0.5rem; }
|
|
46
|
-
.mode-status b { color:
|
|
47
|
-
.mode-status small { display: block; color:
|
|
58
|
+
.mode-status b { color: var(--text); }
|
|
59
|
+
.mode-status small { display: block; color: var(--text3); font-size: 0.75rem; }
|
|
48
60
|
.sw { position: relative; width: 38px; height: 22px; flex-shrink: 0; }
|
|
49
61
|
.sw input { opacity: 0; width: 0; height: 0; }
|
|
50
|
-
.sw span { position: absolute; inset: 0; background:
|
|
62
|
+
.sw span { position: absolute; inset: 0; background: var(--border2); border-radius: 11px; cursor: pointer; transition: 0.2s; }
|
|
51
63
|
.sw span::before { content: ''; position: absolute; width: 16px; height: 16px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: 0.2s; }
|
|
52
|
-
.sw input:checked + span { background:
|
|
64
|
+
.sw input:checked + span { background: var(--accent); }
|
|
53
65
|
.sw input:checked + span::before { transform: translateX(16px); }
|
|
54
|
-
.dir-pick { border: 1px solid
|
|
55
|
-
.dir-pick div { padding: 0.4rem 0.6rem; cursor: pointer; color:
|
|
56
|
-
.dir-pick div:hover { background:
|
|
57
|
-
.dir-pick .cur { color:
|
|
66
|
+
.dir-pick { border: 1px solid var(--border); border-radius: 8px; max-height: 220px; overflow-y: auto; margin-bottom: 0.5rem; font-size: 0.8rem; }
|
|
67
|
+
.dir-pick div { padding: 0.4rem 0.6rem; cursor: pointer; color: var(--text2); }
|
|
68
|
+
.dir-pick div:hover { background: var(--line); }
|
|
69
|
+
.dir-pick .cur { color: var(--text3); font-size: 0.72rem; cursor: default; border-bottom: 1px solid var(--line); }
|
|
58
70
|
.msg { font-size: 0.8rem; margin-top: 0.3rem; min-height: 1.2em; }
|
|
59
71
|
.msg-ok { color: #3fb950; }
|
|
60
72
|
.msg-err { color: #f85149; }
|
|
61
|
-
.status-msg { text-align: center; padding: 1rem; color:
|
|
62
|
-
.uptime { font-size: 0.78rem; color:
|
|
73
|
+
.status-msg { text-align: center; padding: 1rem; color: var(--text3); font-size: 0.85rem; }
|
|
74
|
+
.uptime { font-size: 0.78rem; color: var(--muted); text-align: center; margin-top: 1rem; }
|
|
63
75
|
</style>
|
|
64
76
|
</head>
|
|
65
77
|
<body>
|
|
@@ -81,7 +93,7 @@
|
|
|
81
93
|
|
|
82
94
|
<!-- 版本 + 升级 -->
|
|
83
95
|
<div class="ver-card">
|
|
84
|
-
<div style="font-size:0.8rem;color
|
|
96
|
+
<div style="font-size:0.8rem;color:var(--text3)">当前版本</div>
|
|
85
97
|
<div class="ver-big" id="v-cur">--</div>
|
|
86
98
|
<div class="ver-sub" id="v-latest"></div>
|
|
87
99
|
</div>
|
|
@@ -93,7 +105,7 @@
|
|
|
93
105
|
|
|
94
106
|
<!-- Claude Code 版本 + 升级 -->
|
|
95
107
|
<div class="ver-card">
|
|
96
|
-
<div style="font-size:0.8rem;color
|
|
108
|
+
<div style="font-size:0.8rem;color:var(--text3)">Claude Code 版本</div>
|
|
97
109
|
<div class="ver-big" id="cl-cur">--</div>
|
|
98
110
|
<div class="ver-sub" id="cl-latest"></div>
|
|
99
111
|
</div>
|
|
@@ -105,13 +117,13 @@
|
|
|
105
117
|
<!-- 在线用户 -->
|
|
106
118
|
<div class="card">
|
|
107
119
|
<h3>在线用户</h3>
|
|
108
|
-
<div id="online-users"><div class="row" style="color
|
|
120
|
+
<div id="online-users"><div class="row" style="color:var(--muted)">加载中...</div></div>
|
|
109
121
|
</div>
|
|
110
122
|
|
|
111
123
|
<!-- 活跃会话 -->
|
|
112
124
|
<div class="card">
|
|
113
125
|
<h3>活跃会话</h3>
|
|
114
|
-
<div id="active-sessions"><div class="row" style="color
|
|
126
|
+
<div id="active-sessions"><div class="row" style="color:var(--muted)">加载中...</div></div>
|
|
115
127
|
</div>
|
|
116
128
|
|
|
117
129
|
<!-- 用户管理 -->
|
|
@@ -120,7 +132,7 @@
|
|
|
120
132
|
<div id="mode-status" class="mode-status"></div>
|
|
121
133
|
<div id="user-list"></div>
|
|
122
134
|
<div id="excl-row" class="row" style="display:none">
|
|
123
|
-
<span>独占模式<br><small style="color
|
|
135
|
+
<span>独占模式<br><small style="color:var(--muted)">同一时间只允许一个人登录,别人登录会被挡住</small></span>
|
|
124
136
|
<label class="sw"><input type="checkbox" id="excl-toggle" onchange="setExclusive(this.checked)"><span></span></label>
|
|
125
137
|
</div>
|
|
126
138
|
<button class="btn-sm" id="add-open" onclick="openAddMember()" style="margin-top:0.6rem;width:100%"></button>
|
|
@@ -141,7 +153,7 @@
|
|
|
141
153
|
</div>
|
|
142
154
|
</div>
|
|
143
155
|
<div class="msg" id="u-msg"></div>
|
|
144
|
-
<div id="mode-foot" style="font-size:0.72rem;color
|
|
156
|
+
<div id="mode-foot" style="font-size:0.72rem;color:var(--muted);margin-top:0.5rem;line-height:1.5"></div>
|
|
145
157
|
</div>
|
|
146
158
|
|
|
147
159
|
<!-- 修改管理密码 -->
|
|
@@ -157,8 +169,8 @@
|
|
|
157
169
|
<!-- 大模型网关 -->
|
|
158
170
|
<div class="card">
|
|
159
171
|
<h3>大模型网关</h3>
|
|
160
|
-
<div style="font-size:0.75rem;color
|
|
161
|
-
配置后新建的 Claude 会话自动注入 <code style="background
|
|
172
|
+
<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.6rem;line-height:1.5">
|
|
173
|
+
配置后新建的 Claude 会话自动注入 <code style="background:var(--surface);padding:0.1em 0.3em;border-radius:3px">ANTHROPIC_BASE_URL</code> / <code style="background:var(--surface);padding:0.1em 0.3em;border-radius:3px">ANTHROPIC_API_KEY</code>
|
|
162
174
|
</div>
|
|
163
175
|
<div class="inp-row">
|
|
164
176
|
<input type="text" class="inp" id="gw-url" placeholder="网关 URL(如 https://gateway.example.com)">
|
|
@@ -173,11 +185,11 @@
|
|
|
173
185
|
<!-- 命名密钥库 -->
|
|
174
186
|
<div class="card">
|
|
175
187
|
<h3>密钥库(Secret Vault)</h3>
|
|
176
|
-
<div style="font-size:0.75rem;color
|
|
188
|
+
<div style="font-size:0.75rem;color:var(--muted);margin-bottom:0.6rem;line-height:1.5">
|
|
177
189
|
集中存 GitLab PAT / 部署 token / 网关 key / 证书等敏感值,spawn 会话时注入成环境变量,AI/命令用
|
|
178
|
-
<code style="background
|
|
190
|
+
<code style="background:var(--surface);padding:0.1em 0.3em;border-radius:3px">$名字</code>
|
|
179
191
|
引用——密钥值不进聊天、不落 transcript。类型选「文件」(证书等)时注入的是路径
|
|
180
|
-
<code style="background
|
|
192
|
+
<code style="background:var(--surface);padding:0.1em 0.3em;border-radius:3px">$MYHI_SECRET_名字</code>。
|
|
181
193
|
作用域「项目」仅注给该 cwd 的会话(最小权限)。<b>密钥值绝不写进记忆/CLAUDE.md。</b>
|
|
182
194
|
</div>
|
|
183
195
|
<div id="sec-list" style="margin-bottom:0.6rem"></div>
|
|
@@ -245,22 +257,22 @@ async function loadSecrets() {
|
|
|
245
257
|
const d = await r.json();
|
|
246
258
|
const box = document.getElementById('sec-list');
|
|
247
259
|
let html = '';
|
|
248
|
-
const row = (s, scope, cwd) => `<div style="display:flex;align-items:center;gap:0.5rem;padding:0.25rem 0;border-bottom:1px solid
|
|
249
|
-
<span style="color
|
|
250
|
-
<span style="color
|
|
251
|
-
<span style="color
|
|
260
|
+
const row = (s, scope, cwd) => `<div style="display:flex;align-items:center;gap:0.5rem;padding:0.25rem 0;border-bottom:1px solid var(--line);font-size:0.8rem">
|
|
261
|
+
<span style="color:var(--text);font-family:monospace">${escHtml(s.name)}</span>
|
|
262
|
+
<span style="color:var(--muted)">${s.type === 'file' ? '📄文件' : '🔑变量'}</span>
|
|
263
|
+
<span style="color:var(--muted);font-family:monospace;flex:1">${escHtml(s.preview)}</span>
|
|
252
264
|
<button class="btn-sm" style="padding:0.1rem 0.5rem" onclick="delSecret('${escHtml(s.name)}','${scope}','${escHtml(cwd || '')}')">删</button>
|
|
253
265
|
</div>`;
|
|
254
266
|
if (d.global && d.global.length) {
|
|
255
|
-
html += '<div style="color
|
|
267
|
+
html += '<div style="color:var(--text3);font-size:0.72rem;margin:0.2rem 0">全局</div>';
|
|
256
268
|
html += d.global.map(s => row(s, 'global', '')).join('');
|
|
257
269
|
}
|
|
258
270
|
for (const p of (d.projects || [])) {
|
|
259
271
|
if (!p.secrets.length) continue;
|
|
260
|
-
html += `<div style="color
|
|
272
|
+
html += `<div style="color:var(--text3);font-size:0.72rem;margin:0.4rem 0 0.2rem;font-family:monospace">项目 ${escHtml(p.cwd)}</div>`;
|
|
261
273
|
html += p.secrets.map(s => row(s, 'project', p.cwd)).join('');
|
|
262
274
|
}
|
|
263
|
-
box.innerHTML = html || '<div style="color
|
|
275
|
+
box.innerHTML = html || '<div style="color:var(--muted);font-size:0.78rem">(暂无密钥)</div>';
|
|
264
276
|
} catch {}
|
|
265
277
|
}
|
|
266
278
|
|
|
@@ -356,11 +368,16 @@ async function refresh() {
|
|
|
356
368
|
const cr = await fetch('/api/claude/version', { headers: H() });
|
|
357
369
|
if (cr.ok) {
|
|
358
370
|
const cd = await cr.json();
|
|
359
|
-
document.getElementById('cl-cur').textContent = cd.current ? ('v' + cd.current) : '
|
|
371
|
+
document.getElementById('cl-cur').textContent = cd.current ? ('v' + cd.current) : '未安装';
|
|
360
372
|
const clBtn = document.getElementById('btn-cl-up');
|
|
373
|
+
_clInstalled = !!cd.current;
|
|
361
374
|
if (cd.upgrade?.running) {
|
|
362
|
-
document.getElementById('cl-latest').innerHTML = '<span style="color:#58a6ff"
|
|
363
|
-
clBtn.disabled = true; clBtn.textContent = '
|
|
375
|
+
document.getElementById('cl-latest').innerHTML = '<span style="color:#58a6ff">' + (cd.current ? '升级' : '安装') + '中...</span>';
|
|
376
|
+
clBtn.disabled = true; clBtn.textContent = (cd.current ? '升级' : '安装') + '中...';
|
|
377
|
+
} else if (!cd.current) {
|
|
378
|
+
// 没装(npm 装 myhi 不会顺带装它):别显示「已是最新」,给安装按钮
|
|
379
|
+
document.getElementById('cl-latest').innerHTML = '<span style="color:#f0883e">AI 任务需要它,这台电脑上还没有</span>' + (cd.latest ? ',最新版 v' + cd.latest : '');
|
|
380
|
+
clBtn.disabled = false; clBtn.classList.add('glow'); clBtn.textContent = '安装 Claude Code';
|
|
364
381
|
} else if (cd.updateAvailable) {
|
|
365
382
|
document.getElementById('cl-latest').innerHTML = '最新版本: <b>v' + cd.latest + '</b>';
|
|
366
383
|
clBtn.disabled = false; clBtn.classList.add('glow'); clBtn.textContent = '升级到 v' + cd.latest;
|
|
@@ -374,23 +391,23 @@ async function refresh() {
|
|
|
374
391
|
// 在线用户
|
|
375
392
|
const ouEl = document.getElementById('online-users');
|
|
376
393
|
if (!d.onlineUsers?.length) {
|
|
377
|
-
ouEl.innerHTML = '<div class="row" style="color
|
|
394
|
+
ouEl.innerHTML = '<div class="row" style="color:var(--muted)">无在线用户</div>';
|
|
378
395
|
} else {
|
|
379
396
|
ouEl.innerHTML = d.onlineUsers.map(u =>
|
|
380
|
-
`<div class="row"><span>${esc(u.name)}</span><span style="color
|
|
397
|
+
`<div class="row"><span>${esc(u.name)}</span><span style="color:var(--muted)">${esc(u.role)}</span></div>`
|
|
381
398
|
).join('');
|
|
382
399
|
}
|
|
383
400
|
|
|
384
401
|
// 活跃会话
|
|
385
402
|
const asEl = document.getElementById('active-sessions');
|
|
386
403
|
if (!d.sessions?.length) {
|
|
387
|
-
asEl.innerHTML = '<div class="row" style="color
|
|
404
|
+
asEl.innerHTML = '<div class="row" style="color:var(--muted)">无活跃会话</div>';
|
|
388
405
|
} else {
|
|
389
406
|
asEl.innerHTML = d.sessions.map(s => {
|
|
390
407
|
const tag = s.mode === 'agent'
|
|
391
408
|
? '<span style="display:inline-block;font-size:0.7rem;padding:0.1rem 0.4rem;border-radius:4px;background:rgba(124,58,237,0.2);color:#a78bfa">Agent</span>'
|
|
392
409
|
: '<span style="display:inline-block;font-size:0.7rem;padding:0.1rem 0.4rem;border-radius:4px;background:rgba(63,185,80,0.2);color:#3fb950">PTY</span>';
|
|
393
|
-
return `<div class="row"><span>${tag} ${esc(s.title)} <span style="color
|
|
410
|
+
return `<div class="row"><span>${tag} ${esc(s.title)} <span style="color:var(--muted)">${esc(s.owner||'')}</span></span><span style="color:var(--muted)">${s.viewers}人</span></div>`;
|
|
394
411
|
}).join('');
|
|
395
412
|
}
|
|
396
413
|
|
|
@@ -420,7 +437,7 @@ function renderUsers(users, exclusive) {
|
|
|
420
437
|
const el = document.getElementById('user-list');
|
|
421
438
|
el.innerHTML = users.map(u => `
|
|
422
439
|
<div class="row">
|
|
423
|
-
<span><b style="color
|
|
440
|
+
<span><b style="color:var(--text)">${esc(u.name)}</b> <span style="color:var(--muted);font-size:0.72rem">${u.dir ? '📁 ' + esc(u.dir) : '不限目录'}</span></span>
|
|
424
441
|
<div class="row-actions">
|
|
425
442
|
<button onclick="editUserPwd('${esc(u.name)}')">重置密码</button>
|
|
426
443
|
<button class="del" onclick="delUser('${esc(u.name)}')">删除</button>
|
|
@@ -562,10 +579,14 @@ async function doUpgrade(force) {
|
|
|
562
579
|
} catch { fail('连接失败'); }
|
|
563
580
|
}
|
|
564
581
|
|
|
582
|
+
let _clInstalled = true;
|
|
565
583
|
async function doClaudeUpgrade() {
|
|
566
584
|
const btn = document.getElementById('btn-cl-up'), msg = document.getElementById('cl-msg');
|
|
567
|
-
|
|
568
|
-
|
|
585
|
+
const verb = _clInstalled ? '升级' : '安装';
|
|
586
|
+
if (!confirm(_clInstalled
|
|
587
|
+
? '升级 Claude Code?\nmyhi 会用 npm --include=optional 装全平台二进制,避免占位符坑。\n不影响正在跑的会话,升级后新查询生效。'
|
|
588
|
+
: '安装 Claude Code(myhi 的 AI 引擎)?\n下载约 260MB,需要几分钟。装好后还要在这台电脑上运行一次 claude 登录,或配置下方的大模型网关。')) return;
|
|
589
|
+
btn.disabled = true; btn.textContent = verb + '中...'; msg.className = 'status-msg'; msg.textContent = '正在下载安装(~260MB,可能几分钟)...'; msg.style.display = '';
|
|
569
590
|
try {
|
|
570
591
|
const r = await fetch('/api/claude/update', { method: 'POST', headers: H() });
|
|
571
592
|
const d = await r.json();
|
|
@@ -576,7 +597,7 @@ async function doClaudeUpgrade() {
|
|
|
576
597
|
const s = await sr.json();
|
|
577
598
|
if (!s.running) {
|
|
578
599
|
clearInterval(poll);
|
|
579
|
-
if (s.ok) { msg.textContent = '已升级到 v' + s.version; setTimeout(refresh, 500); }
|
|
600
|
+
if (s.ok) { msg.textContent = (_clInstalled ? '已升级到 v' : '已安装 v') + s.version + (_clInstalled ? '' : '。还差一步:在这台电脑上运行一次 claude 登录,或配置下方的大模型网关'); setTimeout(refresh, 500); }
|
|
580
601
|
else { msg.textContent = '失败: ' + (s.error||''); btn.disabled = false; btn.textContent = '重试'; }
|
|
581
602
|
}
|
|
582
603
|
} catch {}
|
package/dist/chat.html
CHANGED
|
@@ -12,14 +12,19 @@
|
|
|
12
12
|
<script src="/lib/machine.js" data-session></script>
|
|
13
13
|
<script src="/lib/prefs.js"></script>
|
|
14
14
|
<script src="/sio-client/socket.io.min.js"></script>
|
|
15
|
+
<script src="/theme.js"></script>
|
|
15
16
|
<script>
|
|
16
17
|
(function(){try{
|
|
17
|
-
var t=localStorage.getItem('myhi_theme')||'dark';
|
|
18
|
+
var t=localStorage.getItem('myhi_theme')||window.MYHI_DEFAULT_THEME||'dark';
|
|
18
19
|
var f=localStorage.getItem('myhi_font')||'medium';
|
|
19
20
|
var r=document.documentElement;
|
|
20
21
|
if(t&&t!=='dark') r.setAttribute('data-theme',t);
|
|
21
22
|
r.setAttribute('data-font',f);
|
|
22
|
-
|
|
23
|
+
var em=new URLSearchParams(location.search).get('embed');
|
|
24
|
+
if(em==='1') r.classList.add('embed');
|
|
25
|
+
// 首页电脑端工作台右侧嵌入:保留本页顶栏,左侧已有任务列表 → 藏掉分屏/切换/返回;
|
|
26
|
+
// 页面里的链接(如「首页 ⚙ 设置」)在整个窗口打开,别在右侧小框里再套一个首页
|
|
27
|
+
if(em==='ws'){ r.classList.add('embed-ws'); var bs=document.createElement('base'); bs.target='_top'; document.head.appendChild(bs); }
|
|
23
28
|
}catch(e){}})();
|
|
24
29
|
</script>
|
|
25
30
|
<style>
|
|
@@ -55,6 +60,7 @@
|
|
|
55
60
|
html[data-font="large"] { font-size: 19.5px; }
|
|
56
61
|
/* 分屏内嵌模式(?embed=1):隐藏本页顶栏,让每个窗格更紧凑(外层 grid.html 提供窗格头) */
|
|
57
62
|
html.embed #top-bar { display: none; }
|
|
63
|
+
html.embed-ws #grid-btn, html.embed-ws [onclick="openSwitchSheet()"], html.embed-ws [onclick="goBack()"] { display: none !important; }
|
|
58
64
|
/* 分屏是 PC 大屏功能,手机端(触屏)隐藏入口 */
|
|
59
65
|
@media (pointer: coarse) { #grid-btn { display: none !important; } }
|
|
60
66
|
|
|
@@ -223,7 +229,8 @@
|
|
|
223
229
|
.msg-synthetic .syn-body { white-space: pre-wrap; font-family: 'SF Mono', 'Cascadia Code', 'Consolas', monospace; font-size: 0.75rem; line-height: 1.5; color: var(--muted); background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.75rem; max-height: 320px; overflow-y: auto; word-break: break-word; }
|
|
224
230
|
|
|
225
231
|
/* ── 输入栏 ─────────────────────────────── */
|
|
226
|
-
|
|
232
|
+
/* 输入区与对话同底色、不画分隔线:输入框自带圆角边框,浮在对话下方即可(原来一条贯穿的横线 + 浅色底带显得割裂) */
|
|
233
|
+
#input-area { background: var(--bg); flex-shrink: 0; padding: 0.5rem 0.75rem 0.35rem; }
|
|
227
234
|
#input-area.no-shortcuts { padding-bottom: max(0.5rem, env(safe-area-inset-bottom)); }
|
|
228
235
|
#input-box { background: var(--bg); border: 1px solid var(--border); border-radius: 12px; transition: border-color 0.2s; overflow: hidden; position: relative; }
|
|
229
236
|
#img-preview { display: none; align-items: center; gap: 0.5rem; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--surface2); background: var(--surface); }
|
|
@@ -302,7 +309,7 @@
|
|
|
302
309
|
#readonly-overlay { display: none; text-align: center; padding: 0.6rem; font-size: 0.8rem; color: var(--muted); background: var(--surface); }
|
|
303
310
|
|
|
304
311
|
/* ── 快捷键栏 ───────────────────────────── */
|
|
305
|
-
#shortcut-bar { background: var(--
|
|
312
|
+
#shortcut-bar { background: var(--bg); padding: 0.35rem 0.5rem; padding-bottom: max(0.35rem, env(safe-area-inset-bottom)); flex-shrink: 0; overflow: hidden; }
|
|
306
313
|
#shortcut-bar > div { display: flex; align-items: center; gap: 0.35rem; overflow-x: auto; scrollbar-width: none; }
|
|
307
314
|
#shortcut-bar > div::-webkit-scrollbar { display: none; }
|
|
308
315
|
.sk { background: var(--surface2); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 0.3rem 0.55rem; font-size: 0.75rem; font-family: 'SF Mono', 'Consolas', monospace; cursor: pointer; white-space: nowrap; flex-shrink: 0; user-select: none; -webkit-user-select: none; touch-action: manipulation; }
|
|
@@ -1234,7 +1241,7 @@
|
|
|
1234
1241
|
syncAppearanceUI();
|
|
1235
1242
|
}
|
|
1236
1243
|
function syncAppearanceUI() {
|
|
1237
|
-
const t = localStorage.getItem('myhi_theme') || 'dark';
|
|
1244
|
+
const t = localStorage.getItem('myhi_theme') || window.MYHI_DEFAULT_THEME || 'dark';
|
|
1238
1245
|
const f = localStorage.getItem('myhi_font') || 'medium';
|
|
1239
1246
|
document.querySelectorAll('#seg-theme .seg').forEach(b => b.classList.toggle('active', b.dataset.theme === t));
|
|
1240
1247
|
document.querySelectorAll('#seg-font .seg').forEach(b => b.classList.toggle('active', b.dataset.font === f));
|
package/dist/grid.html
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
<title>myhi</title>
|
|
8
8
|
<script src="/lib/machine.js"></script>
|
|
9
9
|
<script src="/sio-client/socket.io.min.js"></script>
|
|
10
|
+
<script src="/theme.js"></script>
|
|
10
11
|
<script>
|
|
11
12
|
(function(){try{
|
|
12
|
-
var t=localStorage.getItem('myhi_theme')||'dark';
|
|
13
|
+
var t=localStorage.getItem('myhi_theme')||window.MYHI_DEFAULT_THEME||'dark';
|
|
13
14
|
var f=localStorage.getItem('myhi_font')||'medium';
|
|
14
15
|
var r=document.documentElement;
|
|
15
16
|
if(t&&t!=='dark') r.setAttribute('data-theme',t);
|