wezard 2.0.2 → 2.0.3
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/daemon/mirror-bridge.js +17 -11
- package/dist/daemon/mirror-bridge.js.map +1 -1
- package/dist/shared/chat-render.js +1 -1
- package/package.json +1 -1
- package/web/chat.css +24 -13
- package/web/chat.js +32 -33
|
@@ -38,8 +38,8 @@ export const renderChatPage = () => `<!doctype html><html lang="zh-CN"><head><me
|
|
|
38
38
|
<aside class="side">
|
|
39
39
|
<header class="rolebar" id="rolebar">
|
|
40
40
|
<div class="who" id="rb-who"></div>
|
|
41
|
-
<div class="acts" id="rb-acts"></div>
|
|
42
41
|
</header>
|
|
42
|
+
<div class="acts" id="rb-acts"></div>
|
|
43
43
|
<nav class="convs" id="convs" aria-label="会话"></nav>
|
|
44
44
|
<footer class="sidefoot" id="rb-foot" hidden><div id="rb-sess"></div></footer>
|
|
45
45
|
</aside>
|
package/package.json
CHANGED
package/web/chat.css
CHANGED
|
@@ -43,7 +43,7 @@ button { font: inherit; color: inherit }
|
|
|
43
43
|
display: flex; flex-direction: column; min-height: 0;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/* ── 左栏顶: 当前 role 的名片 (身份
|
|
46
|
+
/* ── 左栏顶: 当前 role 的名片 (身份); 关系/日程入口是名片下、会话列表上的一排 ──── */
|
|
47
47
|
.rolebar {
|
|
48
48
|
position: relative; flex: none; padding: 16px 16px 14px; border-bottom: 1px solid var(--line);
|
|
49
49
|
display: flex; flex-direction: column; gap: 8px; background: var(--card);
|
|
@@ -68,10 +68,12 @@ button { font: inherit; color: inherit }
|
|
|
68
68
|
.rolebar .facts span { white-space: nowrap }
|
|
69
69
|
.rolebar .go { cursor: pointer; color: var(--accent) }
|
|
70
70
|
.rolebar .go:hover { text-decoration: underline }
|
|
71
|
-
.
|
|
72
|
-
.
|
|
71
|
+
.acts { flex: none; display: flex; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--line) }
|
|
72
|
+
.acts:empty { display: none }
|
|
73
|
+
/* 旧外壳 (daemon 未重启) 里它还在名片内 —— 那里不需要自己的内边距与分隔线。 */
|
|
74
|
+
.rolebar .acts { padding: 0; border: 0 }
|
|
73
75
|
.bd {
|
|
74
|
-
font-size:
|
|
76
|
+
flex: 1; font-size: 12.5px; line-height: 28px; padding: 0 10px; border: 0; border-radius: 8px; cursor: pointer;
|
|
75
77
|
background: #f1f3f6; color: var(--mute); white-space: nowrap;
|
|
76
78
|
}
|
|
77
79
|
.bd:hover { color: var(--accent) }
|
|
@@ -155,11 +157,12 @@ button { font: inherit; color: inherit }
|
|
|
155
157
|
.mosaic.n3, .mosaic.n4 { grid-template: 1fr 1fr / 1fr 1fr }
|
|
156
158
|
.mosaic.n3 i:first-child { grid-row: span 2 }
|
|
157
159
|
|
|
158
|
-
.convs { flex: 1; min-height: 0; overflow-y: auto; padding:
|
|
160
|
+
.convs { flex: 1; min-height: 0; overflow-y: auto; padding: 2px 8px 16px }
|
|
159
161
|
.convs h2 {
|
|
160
162
|
margin: 14px 8px 4px; font-size: 12px; font-weight: 600; color: var(--mute);
|
|
161
163
|
display: flex; align-items: baseline; gap: 6px;
|
|
162
164
|
}
|
|
165
|
+
.convs h2:first-child { margin-top: 6px }
|
|
163
166
|
.convs h2 span { font-weight: 400; color: var(--faint); font-variant-numeric: tabular-nums }
|
|
164
167
|
|
|
165
168
|
.ci {
|
|
@@ -179,12 +182,14 @@ button { font: inherit; color: inherit }
|
|
|
179
182
|
.ci .pv { font-size: 12px; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
|
|
180
183
|
.ci .pv:empty { display: none }
|
|
181
184
|
.ci .tag { font-size: 10.5px; border-radius: 4px; padding: 0 4px; flex: none }
|
|
182
|
-
/*
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
.
|
|
185
|
+
/* 头像与名字是进入那个 role 视角的入口 —— 侧栏项、会话头、消息行同一种读法。
|
|
186
|
+
它们嵌在整行那颗按钮里, 所以悬停态必须压过行: 指到入口上时行自己的悬停底色
|
|
187
|
+
让位 (:has), 否则读起来像是要选中这一行。z-index 保证没有兄弟盖住命中区。 */
|
|
188
|
+
.nm.go, .av.go { position: relative; z-index: 1; cursor: pointer }
|
|
189
|
+
.nm.go:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 2px }
|
|
190
|
+
span.av.go { border-radius: 8px; transition: background .12s, box-shadow .12s }
|
|
191
|
+
span.av.go:hover { background: #2f55c924; box-shadow: 0 0 0 3px #2f55c924 }
|
|
192
|
+
.ci:not(.on):has(.go:hover), .si:not(.on):has(.go:hover) { background: transparent }
|
|
188
193
|
.ci .tag.priv { color: var(--priv); background: #9a5b1014 }
|
|
189
194
|
|
|
190
195
|
/* 群里的其他 role —— 挂在群下, 与会话项同一种读法 (名字 · 时刻 / 最近一句), 只压一档。
|
|
@@ -286,9 +291,15 @@ button { font: inherit; color: inherit }
|
|
|
286
291
|
.mrow.mine .mb .bubble, .mrow.mine .mb .typing, .mrow.mine .mb .stats { background: transparent; border-color: var(--mine-line) }
|
|
287
292
|
.mrow.mine .mb .bubble.user, .mrow.mine .mb .bubble.approval,
|
|
288
293
|
.mrow.mine .mb .md-body code, .mrow.mine .mb .md-body pre,
|
|
289
|
-
.mrow.mine .mb .tile, .mrow.mine .mb details > summary,
|
|
294
|
+
.mrow.mine .mb .tile, .mrow.mine .mb details:not(.tool-call) > summary,
|
|
295
|
+
.mrow.mine .mb .tool-body section > h2, .mrow.mine .mb .diff-meta,
|
|
290
296
|
.mrow.mine .mb .tool-call > summary:hover { background: var(--mine-soft); border-color: var(--mine-line) }
|
|
291
297
|
.mrow.mine .mb .md-body pre code { background: transparent }
|
|
298
|
+
/* 展开的工具正文 (command / diff / 文件内容) 与嵌在父轮里的 subagent 卡片都是裸
|
|
299
|
+
section, 吃的是 TURN_CSS 的白底 —— 同样收回气泡底色; subagent 的绿色左边线留着。 */
|
|
300
|
+
.mrow.mine .mb .tool-body section { background: transparent; border-color: var(--mine-line) }
|
|
301
|
+
.mrow.mine .mb .turn-group .turn-group { background: transparent; box-shadow: none; border-color: var(--mine-line) }
|
|
302
|
+
.mrow.mine .mb .turn-group .turn-group.subagent { border-left-color: #1a7f3733 }
|
|
292
303
|
.mrow.mine .mb .bubble details, .mrow.mine .mb .tg-head { border-color: var(--mine-line) }
|
|
293
304
|
.mrow.mine .mb .tool-body { border-left-color: var(--mine-line) }
|
|
294
305
|
/* 工具正文是展开时才取的 (chat.js loadTool) —— 空壳期间说一句在干什么。 */
|
|
@@ -316,7 +327,7 @@ button { font: inherit; color: inherit }
|
|
|
316
327
|
.flip:hover, .flip:focus-visible { background: #2f55c90d; color: var(--accent) }
|
|
317
328
|
.flip[disabled] { cursor: default; background: transparent !important; color: transparent !important }
|
|
318
329
|
.mwho .av.go { cursor: pointer }
|
|
319
|
-
.mwho .av.go:hover { border-color: var(--accent) }
|
|
330
|
+
.mwho .av.go:hover { border-color: var(--accent); background: #2f55c914 }
|
|
320
331
|
|
|
321
332
|
.mrow.mark { justify-content: center }
|
|
322
333
|
.mrow.mark > * { flex: 1; max-width: 520px }
|
package/web/chat.js
CHANGED
|
@@ -128,11 +128,23 @@
|
|
|
128
128
|
return k.label || (n && n.label) || ({ human: '👤', task: '⏰', system: '🧙' })[kindOf(id)] || '🧙';
|
|
129
129
|
};
|
|
130
130
|
// `.name` 的点由 CSS 画 —— 人与定时任务没有地址, 不画点。
|
|
131
|
-
var nm = function (id, name) {
|
|
132
|
-
return '<span class="nm ' + kindOf(id) + '">' + esc(name || roleName(id)) + '</span>';
|
|
133
|
-
};
|
|
134
131
|
var nameOf = function (id) { return (kindOf(id) === 'wizard' ? '.' : '') + roleName(id); };
|
|
135
132
|
var canSwitch = function (id) { return !!id && kindOf(id) !== 'task' && kindOf(id) !== 'system' && id !== 'human:'; };
|
|
133
|
+
// 头像与名字就是进入那个 role 视角的入口 —— 侧栏项、会话头、消息行同一种读法。
|
|
134
|
+
// 是 <span> 不是 <button>: 侧栏里它嵌在整行那颗按钮里。
|
|
135
|
+
var goSpan = function (cls, id, inner) {
|
|
136
|
+
var on = canSwitch(id) && id !== ROLE;
|
|
137
|
+
return '<span class="' + cls + (on ? ' go" data-r="' + esc(id) + '" title="' + esc('切到 ' + nameOf(id) + ' 的视角') : '') + '">' + inner + '</span>';
|
|
138
|
+
};
|
|
139
|
+
var nm = function (id, name, go) {
|
|
140
|
+
var cls = 'nm ' + kindOf(id), txt = esc(name || roleName(id));
|
|
141
|
+
return go ? goSpan(cls, id, txt) : '<span class="' + cls + '">' + txt + '</span>';
|
|
142
|
+
};
|
|
143
|
+
var bindGo = function (root, sel) {
|
|
144
|
+
root.querySelectorAll(sel || '.go[data-r]').forEach(function (b) {
|
|
145
|
+
b.onclick = function (e) { e.stopPropagation(); switchRole(b.getAttribute('data-r')); };
|
|
146
|
+
});
|
|
147
|
+
};
|
|
136
148
|
|
|
137
149
|
// ── markdown 渲染 (只对未渲染过的节点做, 复用节点不重绘) ──
|
|
138
150
|
var md = null;
|
|
@@ -265,29 +277,21 @@
|
|
|
265
277
|
};
|
|
266
278
|
|
|
267
279
|
var avatarOf = function (c, extra) {
|
|
268
|
-
if (c.kind !== 'group') return '
|
|
280
|
+
if (c.kind !== 'group') return goSpan('av', c.peer, esc(c.label) + (extra || ''));
|
|
269
281
|
var ms = membersOf(c).slice(0, 4);
|
|
270
282
|
return '<span class="av mosaic n' + ms.length + '" aria-hidden="true">' +
|
|
271
283
|
ms.map(function (m) { return '<i>' + esc(m.label || roleLabel(m.role)) + '</i>'; }).join('') + '</span>';
|
|
272
284
|
};
|
|
273
285
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
var SWAP = '<svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path d="M3 5.5h9M9.5 3l2.5 2.5L9.5 8M13 10.5H4M6.5 8 4 10.5 6.5 13" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
277
|
-
var swapOf = function (role) {
|
|
278
|
-
return canSwitch(role) && role !== ROLE
|
|
279
|
-
? '<span class="sw" role="button" tabindex="0" data-sw="' + esc(role) + '" title="' + esc('切到 ' + nameOf(role) + ' 的视角') + '">' + SWAP + '</span>'
|
|
280
|
-
: '';
|
|
281
|
-
};
|
|
282
|
-
var line = function (title, sw, ts, pv) {
|
|
283
|
-
return '<span class="b"><span class="l1"><span class="t">' + title + '</span>' + sw +
|
|
286
|
+
var line = function (title, ts, pv) {
|
|
287
|
+
return '<span class="b"><span class="l1"><span class="t">' + title + '</span>' +
|
|
284
288
|
'<span class="ts">' + esc(fmtAgo(ts)) + '</span></span>' +
|
|
285
289
|
'<span class="pv">' + esc(pv) + '</span></span>';
|
|
286
290
|
};
|
|
287
291
|
|
|
288
292
|
var convItem = function (c) {
|
|
289
293
|
var on = c.key === CONV;
|
|
290
|
-
var title = c.kind === 'wizard' ? nm(c.peer, c.name) : '<span class="nm chat">' + esc(c.name) + '</span>';
|
|
294
|
+
var title = c.kind === 'wizard' ? nm(c.peer, c.name, true) : '<span class="nm chat">' + esc(c.name) + '</span>';
|
|
291
295
|
// 只列与我有往来的: 在群里但没和我说过话的人, 点进去也是空的。
|
|
292
296
|
var talked = on ? c.subs.filter(function (s) { return s.count; }) : [];
|
|
293
297
|
var subs = talked.length
|
|
@@ -295,13 +299,13 @@
|
|
|
295
299
|
var sel = s.role === WITH;
|
|
296
300
|
return '<button class="si' + (sel ? ' on' : '') + '" data-conv="' + esc(c.key) + '" data-with="' + esc(s.role) + '" ' +
|
|
297
301
|
'title="' + esc('我与 ' + nameOf(s.role) + ' 在这里的 ' + s.count + ' 条往来') + '">' +
|
|
298
|
-
'
|
|
299
|
-
line(nm(s.role, s.name
|
|
302
|
+
goSpan('av', s.role, esc(s.label) + lampOf(s.status)) +
|
|
303
|
+
line(nm(s.role, s.name, true), s.lastTs, s.preview) + '</button>';
|
|
300
304
|
}).join('') + '</div>'
|
|
301
305
|
: '';
|
|
302
306
|
var sel = on && !WITH;
|
|
303
307
|
return '<button class="ci' + (sel ? ' on' : '') + '" data-conv="' + esc(c.key) + '">' +
|
|
304
|
-
avatarOf(c, lampOf(c.status)) + line(title,
|
|
308
|
+
avatarOf(c, lampOf(c.status)) + line(title, c.lastTs, c.preview) +
|
|
305
309
|
'</button>' + subs;
|
|
306
310
|
};
|
|
307
311
|
|
|
@@ -319,11 +323,7 @@
|
|
|
319
323
|
convsEl.querySelectorAll('[data-conv]').forEach(function (b) {
|
|
320
324
|
b.onclick = function () { selectConv(b.getAttribute('data-conv'), b.getAttribute('data-with') || ''); };
|
|
321
325
|
});
|
|
322
|
-
convsEl
|
|
323
|
-
var go = function (e) { e.stopPropagation(); switchRole(x.getAttribute('data-sw')); };
|
|
324
|
-
x.onclick = go;
|
|
325
|
-
x.onkeydown = function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); go(e); } };
|
|
326
|
-
});
|
|
326
|
+
bindGo(convsEl);
|
|
327
327
|
};
|
|
328
328
|
|
|
329
329
|
var shortCwd = function (p) {
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
if (SESS_OPEN && e.key === 'Escape') setSessOpen(false);
|
|
379
379
|
});
|
|
380
380
|
|
|
381
|
-
// 名片: 身份 · 出身 · cwd ·
|
|
381
|
+
// 名片: 身份 · 出身 · cwd · 出生; 名片下面一排是关系 / 日程入口。
|
|
382
382
|
var renderRole = function () {
|
|
383
383
|
var r = R.role;
|
|
384
384
|
if (!r) return;
|
|
@@ -413,7 +413,7 @@
|
|
|
413
413
|
$('#rb-foot').hidden = R.sessions.length < 2;
|
|
414
414
|
bindSessPicker();
|
|
415
415
|
var acts = [];
|
|
416
|
-
//
|
|
416
|
+
// 入口只在有东西可看时出现 —— 挂在名片下、会话列表上, 不挤进名片: 名片的主角是身份。
|
|
417
417
|
if (R.relations) acts.push('<button class="bd' + (VIEW === 'world' ? ' on' : '') + '" data-view="world">关系图</button>');
|
|
418
418
|
if (R.schedules) acts.push('<button class="bd' + (VIEW === 'plan' ? ' on' : '') + '" data-view="plan">日程<b>' + R.schedules + '</b></button>');
|
|
419
419
|
$('#rb-acts').innerHTML = acts.join('');
|
|
@@ -497,7 +497,7 @@
|
|
|
497
497
|
var c = convOf(CONV);
|
|
498
498
|
if (!c) { who.innerHTML = ''; acts.innerHTML = ''; return; }
|
|
499
499
|
if (c.kind === 'wizard') {
|
|
500
|
-
who.innerHTML = avatarOf(c) + '<span class="l"><span class="t">' + nm(c.peer, c.name) + '</span>' +
|
|
500
|
+
who.innerHTML = avatarOf(c) + '<span class="l"><span class="t">' + nm(c.peer, c.name, true) + '</span>' +
|
|
501
501
|
'<span class="sub">私聊 · ' + c.count + ' 条</span></span>';
|
|
502
502
|
} else {
|
|
503
503
|
var ms = membersOf(c);
|
|
@@ -507,8 +507,9 @@
|
|
|
507
507
|
(ms.length > 8 ? '…' : '') + '</span></span>';
|
|
508
508
|
}
|
|
509
509
|
acts.innerHTML = WITH
|
|
510
|
-
? '<span class="with">只看我与 ' + nm(WITH) + '</span><button class="vb" id="ch-all">看全部</button>'
|
|
510
|
+
? '<span class="with">只看我与 ' + nm(WITH, '', true) + '</span><button class="vb" id="ch-all">看全部</button>'
|
|
511
511
|
: '';
|
|
512
|
+
bindGo(who); bindGo(acts);
|
|
512
513
|
var x = $('#ch-all');
|
|
513
514
|
if (x) x.onclick = function () { selectConv(CONV, ''); };
|
|
514
515
|
};
|
|
@@ -529,11 +530,11 @@
|
|
|
529
530
|
// 看 key 而不是会话列表: 换视角就地重包时, 列表还是上一个 role 的。
|
|
530
531
|
var group = CONV.indexOf('p:') !== 0;
|
|
531
532
|
// 群里我不是收信方的那条 (X → Y), 头上写清是说给谁的。
|
|
532
|
-
var to = !mine && m.to !== ROLE && group ? '<span class="to">→ ' + nm(m.to, m.toName) + '</span>' : '';
|
|
533
|
+
var to = !mine && m.to !== ROLE && group ? '<span class="to">→ ' + nm(m.to, m.toName, true) + '</span>' : '';
|
|
533
534
|
var priv = !m.channel && group ? '<span class="ch priv">私聊</span>' : '';
|
|
534
535
|
var who = mine
|
|
535
|
-
? '<span class="to">' + (m.to && m.to !== 'human:' ? '→ ' + nm(m.to, m.toName) : '') + '</span>' + stamp(m.ts)
|
|
536
|
-
: nm(m.from, m.fromName) + to + priv + stamp(m.ts);
|
|
536
|
+
? '<span class="to">' + (m.to && m.to !== 'human:' ? '→ ' + nm(m.to, m.toName, true) : '') + '</span>' + stamp(m.ts)
|
|
537
|
+
: nm(m.from, m.fromName, true) + to + priv + stamp(m.ts);
|
|
537
538
|
var sw = canSwitch(other);
|
|
538
539
|
var flip = '<button class="flip" data-r="' + esc(other) + '"' + (sw ? '' : ' disabled tabindex="-1"') +
|
|
539
540
|
' aria-label="' + esc(sw ? '切到 ' + nameOf(other) + ' 的视角' : '') + '">' +
|
|
@@ -590,9 +591,7 @@
|
|
|
590
591
|
};
|
|
591
592
|
|
|
592
593
|
var bindRow = function (el) {
|
|
593
|
-
el
|
|
594
|
-
b.onclick = function () { switchRole(b.getAttribute('data-r')); };
|
|
595
|
-
});
|
|
594
|
+
bindGo(el, '.flip[data-r]:not([disabled]), .mwho .go[data-r]');
|
|
596
595
|
};
|
|
597
596
|
var rowNode = function (id) {
|
|
598
597
|
var list = inner.querySelectorAll('.mrow');
|